Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 49 additions & 11 deletions pkgs/jnigen/lib/src/config/yaml_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,55 @@ class YamlReader {
/// from YAML config.
Uri? getPath(String property) => _config.optionalPath(property);

List<String>? getStringList(String property) => _config.optionalStringList(
property,
splitCliPattern: ';',
combineAllConfigs: false,
);

List<Uri>? getPathList(String property) => _config.optionalPathList(
property,
combineAllConfigs: false,
splitCliPattern: ';',
);
/// Reports an entry left empty in [property], which reads as null.
///
/// A list read from the config file is cast lazily, so such an entry only
/// fails where something reaches for it, with a message that names neither
/// the property nor the file. A list that came from `-D` holds its own
/// strings and cannot carry a null.
///
/// Pass the list through `cast<Object?>()`: reading an element of the cast
/// list throws before this can see it, and casting again unwraps to the
/// source rather than stacking on top of it.
void _checkForEmptyEntries(String property, List<Object?> values) {
for (var i = 0; i < values.length; ++i) {
if (values[i] == null) {
throw ConfigException(
'Entry ${i + 1} of "$property" is empty in the config file.');
}
}
}

List<String>? getStringList(String property) {
final values = _config.optionalStringList(
property,
splitCliPattern: ';',
combineAllConfigs: false,
);
if (values == null) return null;
// The cast is what lets the check read a null; see the helper.
_checkForEmptyEntries(property, values.cast<Object?>());
return values;
}

List<Uri>? getPathList(String property) {
// `optionalPathList` reads the entries itself, so they have to be checked
// before it does.
final values = _config.optionalStringList(
property,
splitCliPattern: ';',
combineAllConfigs: false,
);
if (values != null) {
// The cast is what lets the check read a null; see the helper.
_checkForEmptyEntries(property, values.cast<Object?>());
}
return _config.optionalPathList(
property,
combineAllConfigs: false,
splitCliPattern: ';',
);
}

String? getOneOf(String property, Set<String> values) =>
_config.optionalString(property, validValues: values);
Expand Down
21 changes: 21 additions & 0 deletions pkgs/jnigen/test/config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,26 @@ void main() async {
name: 'Nested class specified',
overrides: ['-Dclasses=com.android.Clock\$Clock'],
);

for (final property in ['classes', 'source_path']) {
test('Empty entry in $property', () {
// Not expressible as an override: the -D parser requires a value.
final dir = Directory.systemTemp.createTempSync('jnigen_config_test');
addTearDown(() => dir.deleteSync(recursive: true));
final yaml = File(join(dir.path, 'jnigen.yaml'))..writeAsStringSync('''
output:
dart:
path: lib/gen.dart
structure: single_file
$property:
- "com.example.Foo"
-
${property == 'classes' ? '' : 'classes:\n - "com.example.Foo"\n'}''');
expect(
() => Config.parseArgs(['--config', yaml.path]),
throwsA(isA<ConfigException>()),
);
});
}
});
}