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