blob: 458028519e8cb9c9dc3b6dd4aeebddfe3bb8e80c [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Merges a [defaults] options set with an [overrides] options set using
/// simple override semantics, suitable for merging two configurations where
/// one defines default values that are added to (and possibly overridden) by an
/// overriding one.
///
/// The merge rules are:
///
/// * Lists are concatenated without duplicates.
/// * A list of strings is promoted to a map of strings to `true` when merged
/// with another map of strings to booleans. For example `['opt1', 'opt2']`
/// is promoted to `{'opt1': true, 'opt2': true}`.
/// * Maps unioned. When both have the same key, the corresponding values are
/// merged, recursively.
/// * Otherwise, a non-`null` override replaces a default value.
Object? merge(Object? defaults, Object? overrides) {
return switch ((defaults, overrides)) {
(List(isAllStrings: true) && var list, Map(isToBools: true)) => merge(
_promoteList(list),
overrides,
),
(Map(isToBools: true), List(isAllStrings: true) && var list) => merge(
defaults,
_promoteList(list),
),
(Map defaultsMap, Map overridesMap) => _mergeMap(defaultsMap, overridesMap),
(List defaultsList, List overridesList) => _mergeList(
defaultsList,
overridesList,
),
(_, null) =>
// Default to override, unless the overriding value is `null`.
defaults,
_ => overrides,
};
}
/// Promote a list of strings to a map of those strings to `true`.
Map<Object?, Object?> _promoteList(List<Object?> list) {
return {for (var element in list) element: true};
}
/// Merge lists, avoiding duplicates.
List<Object?> _mergeList(List<Object?> defaults, List<Object?> overrides) {
// Add them both to a set so that the overrides replace the defaults.
return {...defaults, ...overrides}.toList();
}
/// Merge maps (recursively).
Map<Object?, Object?> _mergeMap(
Map<Object?, Object?> defaults,
Map<Object?, Object?> overrides,
) {
var merged = {...defaults};
overrides.forEach((key, value) {
merged.update(
key,
(defaultValue) => merge(defaultValue, value),
ifAbsent: () => value,
);
});
return merged;
}
extension<T> on List<T> {
bool get isAllStrings => every((e) => e is String);
}
extension<K, V> on Map<K, V> {
bool get isToBools => values.every((v) => v is bool);
}