Skip to content

Commit

Permalink
[cli_config] Support non-String map keys (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcharkes authored Apr 6, 2023
1 parent 0304fbb commit cd799be
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 19 deletions.
5 changes: 5 additions & 0 deletions pkgs/cli_config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.1.1

- Support non-String map keys in YAML configuration files.
- Support null values in YAML configuration files.

## 0.1.0

- Initial version.
2 changes: 1 addition & 1 deletion pkgs/cli_config/lib/src/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class Config {
Uri? fileSourceUri,
}) {
// Parse config file.
final fileConfig = FileParser().parseMap(fileParsed);
final fileConfig = FileParser().parseToplevelMap(fileParsed);

// Parse CLI argument defines.
final cliConfig = DefinesParser().parse(commandLineDefines);
Expand Down
25 changes: 13 additions & 12 deletions pkgs/cli_config/lib/src/file_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import 'package:yaml/yaml.dart';

class FileParser {
Map<String, dynamic> parse(
Map<String, Object?> parse(
String fileContents, {
Uri? sourceUrl,
}) {
Expand All @@ -16,24 +16,25 @@ class FileParser {
if (parsedYaml is! Map) {
throw FormatException('YAML config must be set of key value pairs.');
}
return parseMap(parsedYaml);
return parseToplevelMap(parsedYaml);
}

Map<String, Object> parseMap(Map<dynamic, dynamic> input) => {
for (final entry in input.entries)
parseKey(entry.key as String): parseValue(entry.value as Object),
};

Object parseValue(Object value) {
if (value is Map) {
return parseMap(value);
Map<String, Object?> parseToplevelMap(Map<dynamic, dynamic> input) {
final result = <String, Object?>{};
for (final entry in input.entries) {
final key = parseToplevelKey(entry.key);
final value = entry.value as Object?;
result[key] = value;
}
return value;
return result;
}

static final _keyRegex = RegExp('([a-z-_]+)');

String parseKey(String key) {
String parseToplevelKey(Object? key) {
if (key is! String) {
throw FormatException("Key '$key' is not a String.");
}
final match = _keyRegex.matchAsPrefix(key);
if (match == null || match.group(0) != key) {
throw FormatException("Define '$key' does not match expected pattern "
Expand Down
2 changes: 1 addition & 1 deletion pkgs/cli_config/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: cli_config
description: A library to take config values from configuration files, CLI arguments, and environment variables.
version: 0.1.0
version: 0.1.1

repository: https://github.com/dart-lang/tools/tree/main/pkgs/cli_config

Expand Down
61 changes: 56 additions & 5 deletions pkgs/cli_config/test/cli_config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,20 @@ void main() {
expect(() => Config.fromConfigFileContents(fileContents: "['asdf']"),
throwsFormatException);
expect(
() => Config.fromConfigFileContents(fileContents: '''foo:
bar:
WRONGKEY:
1
'''),
() => Config.fromConfigFileContents(
fileContents: '''
WRONGKEY:
1
'''
.trim()),
throwsFormatException,
);
expect(
() => Config.fromConfigFileContents(
fileContents: '''
1: 'asdf'
'''
.trim()),
throwsFormatException,
);
});
Expand Down Expand Up @@ -504,4 +513,46 @@ void main() {
expect(config.pathList('my_list'), <String>[]);
}
});

test('non-string key maps', () {
// This is valid in YAML (not in JSON).
//
// Such values cannot be accessed with our hierarchical keys, but they can
// be accessed with [Config.valueOf].
final config = Config(
fileParsed: {
'my_non_string_key_map': {
1: 'asdf',
2: 'foo',
},
},
);

expect(
config.valueOf<Map<Object, Object?>>('my_non_string_key_map'),
{
1: 'asdf',
2: 'foo',
},
);
});

test('null values in maps', () {
final config = Config(
fileParsed: {
'my_non_string_key_map': {
'x': null,
'y': 42,
},
},
);

expect(
config.valueOf<Map<Object, Object?>>('my_non_string_key_map'),
{
'x': null,
'y': 42,
},
);
});
}

0 comments on commit cd799be

Please sign in to comment.