| // 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); |
| } |