Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions packages/config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.1

- **FEAT**(config): Support multiple configuration sources for an option (#127).

## 0.9.0

- Bumped minimum Dart SDK version to 3.6
Expand Down
19 changes: 17 additions & 2 deletions packages/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ The configuration library resolves each option value in a specific order, with e

3. **Configuration files**
- Values from configuration files (e.g. YAML/JSON)
- Lookup key is specified using `configKey`
- Lookup key is specified using `configKey` or `configKeys`
(multiple keys are tried in order of precedence)

4. **Custom value providers**
- Values from custom callbacks
Expand Down Expand Up @@ -349,14 +350,28 @@ YAML file and the JSON pointer syntax is used.
));
```

An option can look up values from multiple configuration sources by specifying
`configKeys` in order of precedence. The first key that yields a value is used:

```dart
dir(DirOption(
argName: 'dir',
configKeys: ['local:/dir', 'settings:/my_dir'],
helpText: 'the local directory',
)),
```

`configKey` can still be used for a single source. If both are specified,
`configKey` is tried first, then each key in `configKeys`.

See the full example in [example/config_file_example.dart](example/config_file_example.dart).

### Multiple configuration sources

By using the `MultiDomainConfigBroker`, configuration sources
from multiple providers can be used, called configuration *domains*.

They are distinguished by the format used in the configKey,
They are distinguished by the format used in the config key,
which needs to specify a so-called *qualified key* -
qualifying the key with the domain it is found in.

Expand Down
7 changes: 7 additions & 0 deletions packages/config/lib/src/config/config_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class ConfigParser implements ArgParser {
final List<String> aliases = const [],
final String? envName,
final String? configKey,
final List<String>? configKeys,
final bool? Function(Configuration cfg)? fromCustom,
final bool Function()? fromDefault,
final String? valueHelp,
Expand All @@ -71,6 +72,7 @@ class ConfigParser implements ArgParser {
argAbbrev: abbr,
envName: envName,
configKey: configKey,
configKeys: configKeys,
fromCustom: fromCustom,
fromDefault: fromDefault,
defaultsTo: defaultsTo,
Expand Down Expand Up @@ -106,6 +108,7 @@ class ConfigParser implements ArgParser {
final int? argPos,
final String? envName,
final String? configKey,
final List<String>? configKeys,
Comment thread
christerswahn marked this conversation as resolved.
final String? Function(Configuration cfg)? fromCustom,
final String Function()? fromDefault,
final OptionGroup? group,
Expand All @@ -119,6 +122,7 @@ class ConfigParser implements ArgParser {
argPos: argPos,
envName: envName,
configKey: configKey,
configKeys: configKeys,
fromCustom: fromCustom,
fromDefault: fromDefault,
defaultsTo: defaultsTo,
Expand Down Expand Up @@ -153,6 +157,7 @@ class ConfigParser implements ArgParser {
final List<String> aliases = const [],
final String? envName,
final String? configKey,
final List<String>? configKeys,
final List<String>? Function(Configuration cfg)? fromCustom,
final List<String> Function()? fromDefault,
final OptionGroup? group,
Expand All @@ -167,6 +172,7 @@ class ConfigParser implements ArgParser {
argAbbrev: abbr,
envName: envName,
configKey: configKey,
configKeys: configKeys,
fromCustom: fromCustom,
fromDefault: fromDefault,
defaultsTo: defaultsTo?.toList(),
Expand All @@ -188,6 +194,7 @@ class ConfigParser implements ArgParser {
argAbbrev: abbr,
envName: envName,
configKey: configKey,
configKeys: configKeys,
fromCustom: fromCustom,
fromDefault: fromDefault,
defaultsTo: defaultsTo?.toList(),
Expand Down
2 changes: 1 addition & 1 deletion packages/config/lib/src/config/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class Configuration<O extends OptionDefinition> {
(argName != null && o.option.argName == argName) ||
(argPos != null && o.option.argPos == argPos) ||
(envName != null && o.option.envName == envName) ||
(configKey != null && o.option.configKey == configKey);
(configKey != null && o.option.allConfigKeys.contains(configKey));
});
}

Expand Down
2 changes: 2 additions & 0 deletions packages/config/lib/src/config/file_system_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class DirOption extends ConfigOptionBase<Directory> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -109,6 +110,7 @@ class FileOption extends ConfigOptionBase<File> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down
9 changes: 9 additions & 0 deletions packages/config/lib/src/config/option_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class StringOption extends ConfigOptionBase<String> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand All @@ -46,6 +47,7 @@ class MultiStringOption extends MultiOption<String> {
super.argAbbrev,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand All @@ -68,6 +70,7 @@ class MultiStringOption extends MultiOption<String> {
super.argAbbrev,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -134,6 +137,7 @@ class EnumOption<E extends Enum> extends ConfigOptionBase<E> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -173,6 +177,7 @@ class ComparableValueOption<V extends Comparable> extends ConfigOptionBase<V> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -230,6 +235,7 @@ class IntOption extends ComparableValueOption<int> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -279,6 +285,7 @@ class DateTimeOption extends ComparableValueOption<DateTime> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -397,6 +404,7 @@ class DurationOption extends ComparableValueOption<Duration> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand All @@ -422,6 +430,7 @@ class DurationOption extends ComparableValueOption<Duration> {
super.argPos,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down
47 changes: 41 additions & 6 deletions packages/config/lib/src/config/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ abstract class ValueParser<V> {
/// 1. Named command line argument
/// 2. Positional command line argument
/// 3. Environment variable
/// 4. By lookup key in configuration sources (such as files)
/// 4. By lookup key in configuration sources (such as files).
/// If multiple keys are specified, they are tried in order.
/// 5. A custom callback function
/// 6. Default value
///
Expand Down Expand Up @@ -185,7 +186,19 @@ abstract class ConfigOptionBase<V> implements OptionDefinition<V> {
final String? argAbbrev;
final int? argPos;
final String? envName;

/// A single configuration source lookup key.
///
/// For multiple sources, use [configKeys] instead, or specify both:
/// [configKey] is tried first, then each key in [configKeys].
final String? configKey;

/// Configuration source lookup keys, in order of precedence.
///
/// Keys are tried in order until a value is found.
/// If [configKey] is also set, it is tried first.
final List<String>? configKeys;

final V? Function(Configuration cfg)? fromCustom;
final V Function()? fromDefault;
final V? defaultsTo;
Expand All @@ -208,6 +221,7 @@ abstract class ConfigOptionBase<V> implements OptionDefinition<V> {
this.argPos,
this.envName,
this.configKey,
this.configKeys,
this.fromCustom,
this.fromDefault,
this.defaultsTo,
Expand All @@ -221,6 +235,15 @@ abstract class ConfigOptionBase<V> implements OptionDefinition<V> {
this.hide = false,
});

/// Configuration lookup keys in order of precedence.
///
/// Includes [configKey] if set, followed by [configKeys] if set.
List<String> get allConfigKeys {
final key = configKey;
final keys = configKeys;
return [if (key != null) key, ...?keys];
}

V? defaultValue() {
final df = fromDefault;
return (df != null ? df() : defaultsTo);
Expand Down Expand Up @@ -310,8 +333,12 @@ abstract class ConfigOptionBase<V> implements OptionDefinition<V> {
if (argPos != null) {
return 'positional argument $argPos';
}
if (configKey != null) {
return 'configuration key `$configKey`';
final keys = allConfigKeys;
if (keys.isNotEmpty) {
if (keys.length == 1) {
return 'configuration key `${keys.single}`';
}
return 'configuration keys ${keys.map((final k) => '`$k`').join(', ')}';
}
return _unnamedOptionString;
}
Expand Down Expand Up @@ -439,9 +466,15 @@ abstract class ConfigOptionBase<V> implements OptionDefinition<V> {
final Configuration cfg,
final ConfigurationBroker? configBroker,
) {
final key = configKey;
if (configBroker == null || key == null) return null;
final value = configBroker.valueOrNull(key, cfg);
if (configBroker == null) return null;
final keys = allConfigKeys;
if (keys.isEmpty) return null;

Object? value;
for (final key in keys) {
value = configBroker.valueOrNull(key, cfg);
if (value != null) break;
}
if (value == null) return null;
if (value is String) {
return OptionResolutionImpl(
Expand Down Expand Up @@ -536,6 +569,7 @@ class FlagOption extends ConfigOptionBase<bool> {
super.argAbbrev,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down Expand Up @@ -628,6 +662,7 @@ class MultiOption<T> extends ConfigOptionBase<List<T>> {
super.argAbbrev,
super.envName,
super.configKey,
super.configKeys,
super.fromCustom,
super.fromDefault,
super.defaultsTo,
Expand Down
2 changes: 1 addition & 1 deletion packages/config/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: config
version: 0.9.0
version: 0.9.1
description: A package for parsing command-line arguments and configuration files.
repository: https://github.com/serverpod/cli_tools
issue_tracker: https://github.com/serverpod/cli_tools/issues
Expand Down
Loading