| // Copyright (c) 2020, 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. | |
| import 'dart:collection' as collection; | |
| import 'package:collection/collection.dart'; | |
| import 'package:source_span/source_span.dart'; | |
| import 'package:yaml/yaml.dart'; | |
| import 'equality.dart'; | |
| import 'utils.dart'; | |
| /// Returns a new [YamlMap] constructed by applying [update] onto the nodes of | |
| /// this [YamlMap]. | |
| YamlMap updatedYamlMap(YamlMap map, Function(Map) update) { | |
| final dummyMap = deepEqualsMap(); | |
| dummyMap.addAll(map.nodes); | |
| update(dummyMap); | |
| return wrapAsYamlNode(dummyMap) as YamlMap; | |
| } | |
| /// Wraps [value] into a [YamlNode]. | |
| /// | |
| /// [Map]s, [List]s and Scalars will be wrapped as [YamlMap]s, [YamlList]s, | |
| /// and [YamlScalar]s respectively. If [collectionStyle]/[scalarStyle] is | |
| /// defined, and [value] is a collection or scalar, the wrapped [YamlNode] will | |
| /// have the respective style, otherwise it defaults to the ANY style. | |
| /// | |
| /// If [value] is a [Map] or [List], then [wrapAsYamlNode] will be called | |
| /// recursively on all children, and [collectionStyle]/[scalarStyle] will be | |
| /// applied to any children that are not instances of [YamlNode]. | |
| /// | |
| /// If a [YamlNode] is passed in, no further wrapping will be done, and the | |
| /// [collectionStyle]/[scalarStyle] will not be applied. | |
| YamlNode wrapAsYamlNode( | |
| Object? value, { | |
| CollectionStyle collectionStyle = CollectionStyle.ANY, | |
| ScalarStyle scalarStyle = ScalarStyle.ANY, | |
| }) { | |
| if (value is YamlScalar) { | |
| assertValidScalar(value.value); | |
| return value; | |
| } else if (value is YamlList) { | |
| for (final item in value.nodes) { | |
| wrapAsYamlNode(item); | |
| } | |
| return value; | |
| } else if (value is YamlMap) { | |
| /// Both [entry.key] and [entry.values] are guaranteed to be [YamlNode]s, | |
| /// so running this will just assert that they are valid scalars. | |
| for (final entry in value.nodes.entries) { | |
| wrapAsYamlNode(entry.key); | |
| wrapAsYamlNode(entry.value); | |
| } | |
| return value; | |
| } else if (value is Map) { | |
| return YamlMapWrap( | |
| value, | |
| collectionStyle: collectionStyle, | |
| scalarStyle: scalarStyle, | |
| ); | |
| } else if (value is List) { | |
| return YamlListWrap( | |
| value, | |
| collectionStyle: collectionStyle, | |
| scalarStyle: scalarStyle, | |
| ); | |
| } else { | |
| assertValidScalar(value); | |
| return YamlScalarWrap(value, style: scalarStyle); | |
| } | |
| } | |
| /// Internal class that allows us to define a constructor on [YamlScalar] | |
| /// which takes in [style] as an argument. | |
| class YamlScalarWrap implements YamlScalar { | |
| /// The [ScalarStyle] to be used for the scalar. | |
| @override | |
| final ScalarStyle style; | |
| @override | |
| final SourceSpan span; | |
| @override | |
| final dynamic value; | |
| YamlScalarWrap(this.value, {this.style = ScalarStyle.ANY, Object? sourceUrl}) | |
| : span = shellSpan(sourceUrl); | |
| @override | |
| String toString() => value.toString(); | |
| } | |
| /// Internal class that allows us to define a constructor on [YamlMap] | |
| /// which takes in [style] as an argument. | |
| class YamlMapWrap | |
| with collection.MapMixin, UnmodifiableMapMixin | |
| implements YamlMap { | |
| /// The [CollectionStyle] to be used for the map. | |
| @override | |
| final CollectionStyle style; | |
| @override | |
| final Map<dynamic, YamlNode> nodes; | |
| @override | |
| final SourceSpan span; | |
| factory YamlMapWrap( | |
| Map dartMap, { | |
| CollectionStyle collectionStyle = CollectionStyle.ANY, | |
| ScalarStyle scalarStyle = ScalarStyle.ANY, | |
| Object? sourceUrl, | |
| }) { | |
| final wrappedMap = deepEqualsMap<dynamic, YamlNode>(); | |
| for (final entry in dartMap.entries) { | |
| final wrappedKey = wrapAsYamlNode( | |
| entry.key, | |
| collectionStyle: collectionStyle, | |
| scalarStyle: scalarStyle, | |
| ); | |
| final wrappedValue = wrapAsYamlNode( | |
| entry.value, | |
| collectionStyle: collectionStyle, | |
| scalarStyle: scalarStyle, | |
| ); | |
| wrappedMap[wrappedKey] = wrappedValue; | |
| } | |
| return YamlMapWrap._( | |
| wrappedMap, | |
| style: collectionStyle, | |
| sourceUrl: sourceUrl, | |
| ); | |
| } | |
| YamlMapWrap._( | |
| this.nodes, { | |
| CollectionStyle style = CollectionStyle.ANY, | |
| Object? sourceUrl, | |
| }) : span = shellSpan(sourceUrl), | |
| style = nodes.isEmpty ? CollectionStyle.FLOW : style; | |
| @override | |
| dynamic operator [](Object? key) => nodes[key]?.value; | |
| @override | |
| Iterable get keys => nodes.keys.map((node) => (node as YamlNode).value); | |
| @override | |
| Map get value => this; | |
| } | |
| /// Internal class that allows us to define a constructor on [YamlList] | |
| /// which takes in [style] as an argument. | |
| class YamlListWrap with collection.ListMixin implements YamlList { | |
| /// The [CollectionStyle] to be used for the list. | |
| @override | |
| final CollectionStyle style; | |
| @override | |
| final List<YamlNode> nodes; | |
| @override | |
| final SourceSpan span; | |
| @override | |
| int get length => nodes.length; | |
| @override | |
| set length(int index) { | |
| throw UnsupportedError('Cannot modify an unmodifiable List'); | |
| } | |
| factory YamlListWrap( | |
| List dartList, { | |
| CollectionStyle collectionStyle = CollectionStyle.ANY, | |
| ScalarStyle scalarStyle = ScalarStyle.ANY, | |
| Object? sourceUrl, | |
| }) { | |
| return YamlListWrap._( | |
| dartList | |
| .map((v) => wrapAsYamlNode( | |
| v, | |
| collectionStyle: collectionStyle, | |
| scalarStyle: scalarStyle, | |
| )) | |
| .toList(), | |
| style: collectionStyle, | |
| sourceUrl: sourceUrl, | |
| ); | |
| } | |
| YamlListWrap._(this.nodes, | |
| {CollectionStyle style = CollectionStyle.ANY, Object? sourceUrl}) | |
| : span = shellSpan(sourceUrl), | |
| style = nodes.isEmpty ? CollectionStyle.FLOW : style; | |
| @override | |
| dynamic operator [](int index) => nodes[index].value; | |
| @override | |
| void operator []=(int index, Object? value) { | |
| throw UnsupportedError('Cannot modify an unmodifiable List'); | |
| } | |
| @override | |
| List get value => this; | |
| } |