Skip to content
Merged
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
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
20 changes: 18 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,29 @@ 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
Loading