mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Make MappingDataNode use string keys (#5783)
* string keys * obsoletions * Fix ValueTupleSerializer * Release notes * fix release note conflict * cleanup * Fix yaml validator & tests * cleanup release notes * a * enumerator allocations * Also sequence enumerator alloc
This commit is contained in:
@@ -35,7 +35,14 @@ END TEMPLATE-->
|
||||
|
||||
### Breaking changes
|
||||
|
||||
*None yet*
|
||||
* Yaml mappings/dictionaries now only support string keys instead of generic nodes
|
||||
* Several MappingDataNode method arguments or return values now use strings instead of a DataNode object
|
||||
* The MappingDataNode class has various helper methods that still accept a ValueDataNode, but these methods are marked as obsolete and may be removed in the future.
|
||||
* yaml validators should use `MappingDataNode.GetKeyNode()` when validating mapping keys, so that errors can print node start & end information
|
||||
* ValueTuple yaml serialization has changed
|
||||
* Previously they would get serialized into a single mapping with one entry (i.e., `{foo : bar }`
|
||||
* Now they serialize into a sequence (i.e., `[foo, bar]`
|
||||
* The ValueTuple serializer will still try to read mappings, but due to the MappingDataNode this may fail if the previously serialized "key" can't be read as a simple string
|
||||
|
||||
### New features
|
||||
|
||||
|
||||
@@ -475,7 +475,7 @@ public sealed class EntityDeserializer :
|
||||
|
||||
foreach (var (key, value) in tileMap.Children)
|
||||
{
|
||||
var yamlTileId = ((ValueDataNode) key).AsInt();
|
||||
var yamlTileId = int.Parse(key, CultureInfo.InvariantCulture);
|
||||
var tileName = ((ValueDataNode) value).Value;
|
||||
if (migrations.TryGetValue(tileName, out var @new))
|
||||
tileName = @new;
|
||||
|
||||
@@ -69,7 +69,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer<MapChunk, MappingData
|
||||
|
||||
var tileDefinitionManager = dependencies.Resolve<ITileDefinitionManager>();
|
||||
|
||||
node.TryGetValue(new ValueDataNode("version"), out var versionNode);
|
||||
node.TryGetValue("version", out var versionNode);
|
||||
var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1;
|
||||
|
||||
for (ushort y = 0; y < chunk.ChunkSize; y++)
|
||||
|
||||
@@ -29,11 +29,9 @@ public sealed class FixtureSerializer : ITypeSerializer<Dictionary<string, Fixtu
|
||||
|
||||
foreach (var subNode in node)
|
||||
{
|
||||
var key = (ValueDataNode)subNode.Key;
|
||||
|
||||
if (!keys.Add(key.Value))
|
||||
if (!keys.Add(subNode.Key))
|
||||
{
|
||||
seq.Add(new ErrorNode(subNode.Key, $"Found duplicate fixture ID {key.Value}"));
|
||||
seq.Add(new ErrorNode(new ValueDataNode(subNode.Key), $"Found duplicate fixture ID {subNode.Key}"));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -50,10 +48,8 @@ public sealed class FixtureSerializer : ITypeSerializer<Dictionary<string, Fixtu
|
||||
|
||||
foreach (var subNode in node)
|
||||
{
|
||||
var key = (ValueDataNode)subNode.Key;
|
||||
|
||||
var fixture = serializationManager.Read<Fixture>(subNode.Value, hookCtx, context, notNullableOverride: true);
|
||||
value.Add(key.Value, fixture);
|
||||
value.Add(subNode.Key, fixture);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -342,8 +342,7 @@ public partial class PrototypeManager
|
||||
{
|
||||
if (abstractNode is not ValueDataNode abstractValueNode)
|
||||
{
|
||||
mapping.Remove(abstractNode);
|
||||
mapping.Add("abstract", "true");
|
||||
mapping["abstract"] = new ValueDataNode("true");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ public sealed class ReplayData
|
||||
ClientSideRecording = clientSideRecording;
|
||||
YamlData = yamlData;
|
||||
|
||||
if (YamlData.TryGet(new ValueDataNode(ReplayConstants.MetaKeyRecordedBy), out ValueDataNode? node)
|
||||
if (YamlData.TryGet(ReplayConstants.MetaKeyRecordedBy, out ValueDataNode? node)
|
||||
&& Guid.TryParse(node.Value, out var guid))
|
||||
{
|
||||
Recorder = new NetUserId(guid);
|
||||
|
||||
@@ -266,27 +266,21 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
|
||||
foreach (var (key, val) in mapping.Children)
|
||||
{
|
||||
if (key is not ValueDataNode valueDataNode)
|
||||
if (!TryGetIndex(key, out var idx))
|
||||
{
|
||||
validatedMapping.Add(new ErrorNode(key, "Key not ValueDataNode."), new InconclusiveNode(val));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryGetIndex(valueDataNode.Value, out var idx))
|
||||
{
|
||||
if (TryGetIncludeMappingPair(includeValidations, valueDataNode.Value, out var validatedNotFoundPair))
|
||||
if (TryGetIncludeMappingPair(includeValidations, key, out var validatedNotFoundPair))
|
||||
{
|
||||
validatedMapping.Add(validatedNotFoundPair.Key, validatedNotFoundPair.Value);
|
||||
continue;
|
||||
}
|
||||
|
||||
var error = new FieldNotFoundErrorNode(valueDataNode, typeof(T));
|
||||
var error = new FieldNotFoundErrorNode(mapping.GetKeyNode(key), typeof(T));
|
||||
|
||||
validatedMapping.Add(error, new InconclusiveNode(val));
|
||||
continue;
|
||||
}
|
||||
|
||||
var keyValidated = serialization.ValidateNode<string>(key, context);
|
||||
var keyValidated = serialization.ValidateNode<string>(mapping.GetKeyNode(key), context);
|
||||
|
||||
ValidationNode valNode;
|
||||
if (IsNull(val))
|
||||
@@ -295,7 +289,7 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
{
|
||||
var error = new ErrorNode(
|
||||
val,
|
||||
$"Field \"{valueDataNode.Value}\" had null value despite not being annotated as nullable.");
|
||||
$"Field \"{key}\" had null value despite not being annotated as nullable.");
|
||||
|
||||
validatedMapping.Add(keyValidated, error);
|
||||
continue;
|
||||
@@ -309,7 +303,7 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
}
|
||||
|
||||
//include node errors override successful nodes on the main datadef
|
||||
if (valNode is not ErrorNode && TryGetIncludeMappingPair(includeValidations, valueDataNode.Value, out var validatedPair))
|
||||
if (valNode is not ErrorNode && TryGetIncludeMappingPair(includeValidations, key, out var validatedPair))
|
||||
{
|
||||
if (validatedPair.Value is ErrorNode)
|
||||
{
|
||||
|
||||
@@ -190,7 +190,8 @@ public partial class SerializationManager
|
||||
{
|
||||
// tag is set on data definition creation
|
||||
if(!processedTags.Add(dfa.Tag!)) continue; //tag was already processed, probably because we are using the same tag in an include
|
||||
var key = new ValueDataNode(dfa.Tag);
|
||||
|
||||
var key = dfa.Tag!;
|
||||
if (parent.TryGetValue(key, out var parentValue))
|
||||
{
|
||||
if (newMapping.TryGetValue(key, out var childValue))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using YamlDotNet.Core;
|
||||
@@ -28,6 +29,7 @@ namespace Robust.Shared.Serialization.Markdown
|
||||
/// </summary>
|
||||
public abstract DataNode? Except(DataNode node);
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public abstract DataNode PushInheritance(DataNode parent);
|
||||
|
||||
public T CopyCast<T>() where T : DataNode
|
||||
@@ -60,6 +62,7 @@ namespace Robust.Shared.Serialization.Markdown
|
||||
|
||||
public abstract T? Except(T node);
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public abstract T PushInheritance(T node);
|
||||
|
||||
public override DataNode? Except(DataNode node)
|
||||
@@ -67,6 +70,7 @@ namespace Robust.Shared.Serialization.Markdown
|
||||
return node is not T tNode ? throw new InvalidNodeTypeException() : Except(tNode);
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override DataNode PushInheritance(DataNode parent)
|
||||
{
|
||||
return parent is not T tNode ? throw new InvalidNodeTypeException() : PushInheritance(tNode);
|
||||
|
||||
@@ -25,10 +25,7 @@ public static class DataNodeHelpers
|
||||
|
||||
foreach (var (k, v) in node)
|
||||
{
|
||||
foreach (var child in GetAllNodes(k))
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
yield return node.GetKeyNode(k);
|
||||
|
||||
foreach (var child in GetAllNodes(v))
|
||||
{
|
||||
|
||||
@@ -85,6 +85,16 @@ public static class DataNodeParser
|
||||
return node;
|
||||
}
|
||||
|
||||
private static string ParseKey(Parser parser)
|
||||
{
|
||||
var ev = parser.Consume<Scalar>();
|
||||
|
||||
if (!ev.Anchor.IsEmpty)
|
||||
throw new NotSupportedException();
|
||||
|
||||
return ev.Value;
|
||||
}
|
||||
|
||||
private static SequenceDataNode ParseSequence(Parser parser, DocumentState state)
|
||||
{
|
||||
var ev = parser.Consume<SequenceStart>();
|
||||
@@ -124,12 +134,11 @@ public static class DataNodeParser
|
||||
MappingEnd mapEnd;
|
||||
while (!parser.TryConsume(out mapEnd))
|
||||
{
|
||||
var key = Parse(parser, state);
|
||||
var key = ParseKey(parser);
|
||||
var value = Parse(parser, state);
|
||||
|
||||
node.Add(key, value);
|
||||
|
||||
unresolvedAlias |= key is DataNodeAlias;
|
||||
unresolvedAlias |= value is DataNodeAlias;
|
||||
}
|
||||
|
||||
@@ -173,17 +182,16 @@ public static class DataNodeParser
|
||||
|
||||
private static void ResolveMappingAliases(MappingDataNode mapping, DocumentState state)
|
||||
{
|
||||
var swaps = new ValueList<(DataNode key, DataNode value)>();
|
||||
var swaps = new ValueList<(string key, DataNode value)>();
|
||||
|
||||
foreach (var (key, value) in mapping)
|
||||
{
|
||||
if (key is not DataNodeAlias && value is not DataNodeAlias)
|
||||
if (value is not DataNodeAlias valueAlias)
|
||||
return;
|
||||
|
||||
var newKey = key is DataNodeAlias keyAlias ? ResolveAlias(keyAlias, state) : key;
|
||||
var newValue = value is DataNodeAlias valueAlias ? ResolveAlias(valueAlias, state) : value;
|
||||
var newValue = ResolveAlias(valueAlias, state);
|
||||
|
||||
swaps.Add((newKey, newValue));
|
||||
swaps.Add((key, newValue));
|
||||
mapping.Remove(key);
|
||||
}
|
||||
|
||||
@@ -242,6 +250,7 @@ public static class DataNodeParser
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override DataNode PushInheritance(DataNode parent)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
||||
@@ -5,25 +5,30 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
{
|
||||
public sealed class MappingDataNode : DataNode<MappingDataNode>, IDictionary<DataNode, DataNode>
|
||||
public sealed class MappingDataNode : DataNode<MappingDataNode>, IDictionary<string, DataNode>
|
||||
{
|
||||
// To fetch nodes by key name with YAML, we NEED a YamlScalarNode.
|
||||
// We use a thread local one to avoid allocating one every fetch, since we just replace the inner value.
|
||||
// Obviously thread local to avoid threading issues.
|
||||
private static readonly ThreadLocal<ValueDataNode> FetchNode =
|
||||
new(() => new ValueDataNode(""));
|
||||
private readonly Dictionary<string, DataNode> _children;
|
||||
private readonly List<KeyValuePair<string,DataNode>> _list;
|
||||
|
||||
private readonly Dictionary<DataNode, DataNode> _children;
|
||||
private readonly List<KeyValuePair<DataNode,DataNode>> _list;
|
||||
/// <summary>
|
||||
/// ValueDataNodes associated with each key. This is used for yaml validation / error reporting.
|
||||
/// I.e., if a key is meant to be an EntityPrototype ID, we want to print an error that points to the
|
||||
/// corresponding yaml lines.
|
||||
/// </summary>
|
||||
private IReadOnlyDictionary<string, ValueDataNode>? _keyNodes;
|
||||
// TODO avoid populating this unless we are running the yaml linter?
|
||||
|
||||
public IReadOnlyDictionary<DataNode, DataNode> Children => _children;
|
||||
public override bool IsEmpty => _children.Count == 0;
|
||||
public int Count => _children.Count;
|
||||
public bool IsReadOnly => false;
|
||||
public IReadOnlyDictionary<string, DataNode> Children => _children;
|
||||
|
||||
public MappingDataNode() : base(NodeMark.Invalid, NodeMark.Invalid)
|
||||
{
|
||||
@@ -41,113 +46,106 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
{
|
||||
_children = new(mapping.Children.Count);
|
||||
_list = new(mapping.Children.Count);
|
||||
foreach (var (key, val) in mapping.Children)
|
||||
var keyNodes = new Dictionary<string, ValueDataNode>(mapping.Children.Count);
|
||||
foreach (var (keyNode, val) in mapping.Children)
|
||||
{
|
||||
Add(key.ToDataNode(), val.ToDataNode());
|
||||
if (keyNode is not YamlScalarNode scalarNode)
|
||||
throw new NotSupportedException("Mapping data nodes must have a scalar keys");
|
||||
|
||||
var valueNode = new ValueDataNode(scalarNode);
|
||||
Add(valueNode.Value, val.ToDataNode());
|
||||
keyNodes.Add(valueNode.Value, valueNode);
|
||||
}
|
||||
|
||||
_keyNodes = keyNodes;
|
||||
Tag = mapping.Tag.IsEmpty ? null : mapping.Tag.Value;
|
||||
}
|
||||
|
||||
public MappingDataNode(Dictionary<DataNode, DataNode> nodes) : base(NodeMark.Invalid, NodeMark.Invalid)
|
||||
public MappingDataNode(Dictionary<string, DataNode> nodes) : base(NodeMark.Invalid, NodeMark.Invalid)
|
||||
{
|
||||
_children = new(nodes.Count);
|
||||
_list = new(nodes.Count);
|
||||
foreach (var (key, val) in nodes)
|
||||
{
|
||||
Add(key, val);
|
||||
}
|
||||
_children = new(nodes);
|
||||
_list = new(_children);
|
||||
}
|
||||
|
||||
public KeyValuePair<DataNode, DataNode> this[int key] => _list[key];
|
||||
|
||||
public DataNode this[string index]
|
||||
{
|
||||
get => Get(index);
|
||||
set => Add(index, value);
|
||||
}
|
||||
public KeyValuePair<string, DataNode> this[int key] => _list[key];
|
||||
|
||||
public MappingDataNode Add(string key, DataNode node)
|
||||
{
|
||||
Add(new ValueDataNode(key), node);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static ValueDataNode GetFetchNode(string key)
|
||||
{
|
||||
var node = FetchNode.Value!;
|
||||
node.Value = key;
|
||||
return node;
|
||||
}
|
||||
|
||||
public MappingDataNode Add(DataNode key, DataNode node)
|
||||
{
|
||||
_children.Add(key, node);
|
||||
_list.Add(new(key, node));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataNode this[DataNode key]
|
||||
public DataNode this[string key]
|
||||
{
|
||||
get => _children[key];
|
||||
set
|
||||
{
|
||||
if (_children.TryAdd(key, value))
|
||||
{
|
||||
_list.Add(new( key, value));
|
||||
_list.Add(new(key, value));
|
||||
return;
|
||||
}
|
||||
|
||||
var i = _list.IndexOf(new(key, _children[key]));
|
||||
_list[i] = new(key, value);
|
||||
var index = IndexOf(key);
|
||||
if (index == -1)
|
||||
throw new Exception("Key exists in Children, but not list?");
|
||||
|
||||
_list[index] = new(key, value);
|
||||
_children[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void IDictionary<DataNode, DataNode>.Add(DataNode key, DataNode value) => Add(key, value);
|
||||
public int IndexOf(string key)
|
||||
{
|
||||
// TODO MappingDataNode
|
||||
// Consider having a Dictionary<string,int> for faster lookups?
|
||||
// IndexOf() gets called in Remove(), which itself gets called frequently (e.g., per serialized component,
|
||||
// per entity, when loading a map.
|
||||
//
|
||||
// Then again, if most mappings only contain 1-4 entries, this list search is comparable in speed, reduces
|
||||
// allocations, and makes adding/inserting entries faster.
|
||||
|
||||
public bool ContainsKey(DataNode key) => _children.ContainsKey(key);
|
||||
for (var index = 0; index < _list.Count; index++)
|
||||
{
|
||||
if (_list[index].Key == key)
|
||||
return index;
|
||||
}
|
||||
|
||||
bool IDictionary<DataNode, DataNode>.Remove(DataNode key)
|
||||
=> ((IDictionary<DataNode, DataNode>)this).Remove(key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool TryGetValue(DataNode key, [NotNullWhen(true)] out DataNode? value) => TryGet(key, out value);
|
||||
void IDictionary<string, DataNode>.Add(string key, DataNode value) => Add(key, value);
|
||||
|
||||
public ICollection<DataNode> Keys => _list.Select(x => x.Key).ToArray();
|
||||
public bool ContainsKey(string key) => _children.ContainsKey(key);
|
||||
|
||||
bool IDictionary<string, DataNode>.Remove(string key)
|
||||
=> ((IDictionary<string, DataNode>)this).Remove(key);
|
||||
|
||||
public bool TryGetValue(string key, [NotNullWhen(true)] out DataNode? value)
|
||||
=> TryGet(key, out value);
|
||||
|
||||
// TODO consider changing these to unsorted collections
|
||||
// I.e., just redirect to _children.Keys to avoid hidden linq & allocations.
|
||||
public ICollection<string> Keys => _list.Select(x => x.Key).ToArray();
|
||||
public ICollection<DataNode> Values => _list.Select(x => x.Value).ToArray();
|
||||
|
||||
public DataNode Get(DataNode key)
|
||||
public DataNode Get(string key)
|
||||
{
|
||||
return _children[key];
|
||||
}
|
||||
|
||||
public T Get<T>(DataNode key) where T : DataNode
|
||||
public T Get<T>(string key) where T : DataNode
|
||||
{
|
||||
return (T) Get(key);
|
||||
}
|
||||
|
||||
public DataNode Get(string key)
|
||||
public bool TryGet(string key, [NotNullWhen(true)] out DataNode? node)
|
||||
{
|
||||
return Get(GetFetchNode(key));
|
||||
return _children.TryGetValue(key, out node);
|
||||
}
|
||||
|
||||
public T Get<T>(string key) where T : DataNode
|
||||
{
|
||||
return Get<T>(GetFetchNode(key));
|
||||
}
|
||||
|
||||
public bool TryGet(DataNode key, [NotNullWhen(true)] out DataNode? node)
|
||||
{
|
||||
if (_children.TryGetValue(key, out node))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGet<T>(DataNode key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
public bool TryGet<T>(string key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
{
|
||||
node = null;
|
||||
if (!TryGet(key, out var rawNode) || rawNode is not T castNode)
|
||||
@@ -156,41 +154,27 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGet(string key, [NotNullWhen(true)] out DataNode? node)
|
||||
{
|
||||
return TryGet(GetFetchNode(key), out node);
|
||||
}
|
||||
|
||||
public bool TryGet<T>(string key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
{
|
||||
return TryGet(GetFetchNode(key), out node);
|
||||
}
|
||||
|
||||
public bool Has(DataNode key)
|
||||
public bool Has(string key)
|
||||
{
|
||||
return _children.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Has(string key)
|
||||
public bool Remove(string key)
|
||||
{
|
||||
return Has(GetFetchNode(key));
|
||||
if (!_children.Remove(key))
|
||||
return false;
|
||||
|
||||
var index = IndexOf(key);
|
||||
if (index == -1)
|
||||
throw new Exception("Key exists in Children, but not list?");
|
||||
|
||||
_list.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public MappingDataNode Remove(DataNode key)
|
||||
public T Cast<T>(string key) where T : DataNode
|
||||
{
|
||||
if (_children.Remove(key, out var val))
|
||||
_list.Remove(new(key, val));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MappingDataNode Remove(string key)
|
||||
{
|
||||
return Remove(GetFetchNode(key));
|
||||
}
|
||||
|
||||
public T Cast<T>(string index) where T : DataNode
|
||||
{
|
||||
return (T) this[index];
|
||||
return (T) this[key];
|
||||
}
|
||||
|
||||
public YamlMappingNode ToYaml()
|
||||
@@ -199,7 +183,23 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
mapping.Add(key.ToYamlNode(), val.ToYamlNode());
|
||||
YamlScalarNode yamlKeyNode;
|
||||
if (_keyNodes != null && _keyNodes.TryGetValue(key, out var keyNode))
|
||||
{
|
||||
yamlKeyNode = (YamlScalarNode)keyNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is matches the ValueDataNode -> YamlScalarNode cast operator
|
||||
yamlKeyNode = new(key)
|
||||
{
|
||||
Style = ValueDataNode.IsNullLiteral(key) || string.IsNullOrWhiteSpace(key)
|
||||
? ScalarStyle.DoubleQuoted
|
||||
: ScalarStyle.Any
|
||||
};
|
||||
}
|
||||
|
||||
mapping.Add(yamlKeyNode, val.ToYamlNode());
|
||||
}
|
||||
|
||||
mapping.Tag = Tag;
|
||||
@@ -207,6 +207,11 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public ValueDataNode GetKeyNode(string key)
|
||||
{
|
||||
return _keyNodes?.GetValueOrDefault(key) ?? new ValueDataNode(key);
|
||||
}
|
||||
|
||||
public MappingDataNode Merge(MappingDataNode otherMapping)
|
||||
{
|
||||
var newMapping = Copy();
|
||||
@@ -227,12 +232,12 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
if (!skipDuplicates || !Has(key))
|
||||
{
|
||||
// Intentionally raises an ArgumentException
|
||||
Add(key.Copy(), val.Copy());
|
||||
Add(key, val.Copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertAt(int index, DataNode key, DataNode value)
|
||||
public void InsertAt(int index, string key, DataNode value)
|
||||
{
|
||||
if (index > _list.Count || index < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@@ -243,20 +248,6 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
_list.Insert(index, new(key, value));
|
||||
}
|
||||
|
||||
public void InsertAt(int index, string key, DataNode value)
|
||||
{
|
||||
if (index > _list.Count || index < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
var k = new ValueDataNode(key);
|
||||
if (!_children.TryAdd(k, value))
|
||||
throw new InvalidOperationException($"Already contains key {key}");
|
||||
|
||||
_list.Insert(index, new(k, value));
|
||||
}
|
||||
|
||||
public override bool IsEmpty => _children.Count == 0;
|
||||
|
||||
public override MappingDataNode Copy()
|
||||
{
|
||||
var newMapping = new MappingDataNode(_children.Count)
|
||||
@@ -268,9 +259,10 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
newMapping.Add(key.Copy(), val.Copy());
|
||||
newMapping.Add(key, val.Copy());
|
||||
}
|
||||
|
||||
newMapping._keyNodes = _keyNodes;
|
||||
return newMapping;
|
||||
}
|
||||
|
||||
@@ -308,17 +300,13 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
var other = node._list.FirstOrNull(p => p.Key.Equals(key));
|
||||
if (other == null)
|
||||
if (!node._children.TryGetValue(key, out var otherVal))
|
||||
{
|
||||
mappingNode.Add(key.Copy(), val.Copy());
|
||||
mappingNode.Add(key, val.Copy());
|
||||
}
|
||||
else
|
||||
else if (val.Except(otherVal) is { } newValue)
|
||||
{
|
||||
// We recursively call except on the values and keep only the differences.
|
||||
var newValue = val.Except(other.Value.Value);
|
||||
if (newValue == null) continue;
|
||||
mappingNode.Add(key.Copy(), newValue);
|
||||
mappingNode.Add(key, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,18 +324,8 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
|
||||
foreach (var (key, val) in _list)
|
||||
{
|
||||
var other = node._list.FirstOrNull(p => p.Key.Equals(key));
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
mappingNode.Add(key.Copy(), val.Copy());
|
||||
}
|
||||
else
|
||||
{
|
||||
// We only keep the entry if the values are not equal
|
||||
if (!val.Equals(other.Value.Value))
|
||||
mappingNode.Add(key.Copy(), val.Copy());
|
||||
}
|
||||
if (!node._children.TryGetValue(key, out var otherVal) || !val.Equals(otherVal))
|
||||
mappingNode.Add(key, val.Copy());
|
||||
}
|
||||
|
||||
return mappingNode._children.Count == 0 ? null : mappingNode;
|
||||
@@ -384,34 +362,24 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
if (_children.Count != other._children.Count)
|
||||
return false;
|
||||
|
||||
if (Tag != other.Tag)
|
||||
return false;
|
||||
|
||||
foreach (var (key, otherValue) in other)
|
||||
{
|
||||
if (!_children.TryGetValue(key, out var ownValue) ||
|
||||
!otherValue.Equals(ownValue))
|
||||
if (!_children.TryGetValue(key, out var ownValue)
|
||||
|| !otherValue.Equals(ownValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Tag == other.Tag;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override MappingDataNode PushInheritance(MappingDataNode node)
|
||||
{
|
||||
var newNode = Copy();
|
||||
foreach (var (key, val) in node)
|
||||
{
|
||||
if(_children.ContainsKey(key))
|
||||
continue;
|
||||
|
||||
newNode.Remove(key);
|
||||
newNode.Add(key.Copy(), val.Copy());
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<DataNode, DataNode>> GetEnumerator() => _list.GetEnumerator();
|
||||
public List<KeyValuePair<string, DataNode>>.Enumerator GetEnumerator() => _list.GetEnumerator();
|
||||
IEnumerator<KeyValuePair<string, DataNode>> IEnumerable<KeyValuePair<string, DataNode>>.GetEnumerator() =>
|
||||
_list.GetEnumerator();
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -430,7 +398,7 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<DataNode, DataNode> item) => Add(item.Key, item.Value);
|
||||
public void Add(KeyValuePair<string, DataNode> item) => Add(item.Key, item.Value);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
@@ -438,18 +406,31 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
_list.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<DataNode, DataNode> item) => _children.ContainsKey(item.Key);
|
||||
public bool Contains(KeyValuePair<string, DataNode> item) => _children.ContainsKey(item.Key);
|
||||
|
||||
public void CopyTo(KeyValuePair<DataNode, DataNode>[] array, int arrayIndex)
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override MappingDataNode PushInheritance(MappingDataNode node)
|
||||
{
|
||||
var newNode = Copy();
|
||||
foreach (var (key, val) in node)
|
||||
{
|
||||
if (_children.ContainsKey(key))
|
||||
continue;
|
||||
|
||||
newNode.Remove(key);
|
||||
newNode.Add(key, val.Copy());
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<string, DataNode>[] array, int arrayIndex)
|
||||
=> _list.CopyTo(array, arrayIndex);
|
||||
|
||||
public bool Remove(KeyValuePair<DataNode, DataNode> item)
|
||||
=> ((IDictionary<DataNode, DataNode>)this).Remove(item.Key);
|
||||
public bool Remove(KeyValuePair<string, DataNode> item)
|
||||
=> ((IDictionary<string, DataNode>) this).Remove(item.Key);
|
||||
|
||||
public int Count => _children.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public bool TryAdd(DataNode key, DataNode value)
|
||||
public bool TryAdd(string key, DataNode value)
|
||||
{
|
||||
if (!_children.TryAdd(key, value))
|
||||
return false;
|
||||
@@ -458,7 +439,7 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAddCopy(DataNode key, DataNode value)
|
||||
public bool TryAddCopy(string key, DataNode value)
|
||||
{
|
||||
ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_children, key, out var exists);
|
||||
if (exists)
|
||||
@@ -468,5 +449,52 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
_list.Add(new(key, entry));
|
||||
return true;
|
||||
}
|
||||
|
||||
// These methods are probably fine to keep around as helper methods, but are currently marked as obsolete
|
||||
// so that people don't uneccesarily allocate a ValueDataNode. I.e., to prevent people from using code like
|
||||
// mapping.TryGet(new ValueDataNode("key"), ...)
|
||||
#region ValueDataNode Helpers
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool TryGet(ValueDataNode key, [NotNullWhen(true)] out DataNode? value)
|
||||
=> TryGet(key.Value, out value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public DataNode this[ValueDataNode key]
|
||||
{
|
||||
get => this[key.Value];
|
||||
set => this[key.Value] = value;
|
||||
}
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool TryGetValue(ValueDataNode key, [NotNullWhen(true)] out DataNode? value)
|
||||
=> TryGet(key.Value, out value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool TryGet<T>(ValueDataNode key, [NotNullWhen(true)] out T? node) where T : DataNode
|
||||
=> TryGet(key.Value, out node);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool Has(ValueDataNode key) => Has(key.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public T Cast<T>(ValueDataNode key) where T : DataNode => Cast<T>(key.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public void Add(KeyValuePair<ValueDataNode, DataNode> item) => Add(item.Key, item.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public MappingDataNode Add(ValueDataNode key, DataNode node) => Add(key.Value, node);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public void InsertAt(int index, ValueDataNode key, DataNode value) => InsertAt(index, key.Value, value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool Contains(KeyValuePair<ValueDataNode, DataNode> item) => _children.ContainsKey(item.Key.Value);
|
||||
|
||||
[Obsolete("Use string keys instead of ValueDataNode")]
|
||||
public bool Remove(ValueDataNode key) => Remove(key.Value);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,15 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
{
|
||||
public static class MappingDataNodeExtensions
|
||||
{
|
||||
public static MappingDataNode Add(this MappingDataNode mapping, string key, DataNode node)
|
||||
{
|
||||
mapping.Add(new ValueDataNode(key), node);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static MappingDataNode Add(this MappingDataNode mapping, string key, string value)
|
||||
{
|
||||
mapping.Add(new ValueDataNode(key), new ValueDataNode(value));
|
||||
mapping.Add(key, new ValueDataNode(value));
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public static MappingDataNode Add(this MappingDataNode mapping, string key, List<string> sequence)
|
||||
{
|
||||
mapping.Add(new ValueDataNode(key), new SequenceDataNode(sequence));
|
||||
mapping.Add(key, new SequenceDataNode(sequence));
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,8 @@ namespace Robust.Shared.Serialization.Markdown.Sequence
|
||||
return newSequence;
|
||||
}
|
||||
|
||||
public IEnumerator<DataNode> GetEnumerator() => _nodes.GetEnumerator();
|
||||
public List<DataNode>.Enumerator GetEnumerator() => _nodes.GetEnumerator();
|
||||
IEnumerator<DataNode> IEnumerable<DataNode>.GetEnumerator() => _nodes.GetEnumerator();
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -192,6 +193,7 @@ namespace Robust.Shared.Serialization.Markdown.Sequence
|
||||
return true;
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override SequenceDataNode PushInheritance(SequenceDataNode node)
|
||||
{
|
||||
var newNode = Copy();
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Robust.Shared.Serialization.Markdown.Value
|
||||
|
||||
public override bool IsEmpty => string.IsNullOrWhiteSpace(Value);
|
||||
|
||||
private static bool IsNullLiteral(string? value) => value != null && value.Trim().ToLower() is "null" ;
|
||||
public static bool IsNullLiteral(string? value) => value != null && value.Trim().ToLower() is "null" ;
|
||||
|
||||
public override ValueDataNode Copy()
|
||||
{
|
||||
@@ -76,6 +76,7 @@ namespace Robust.Shared.Serialization.Markdown.Value
|
||||
return node.Value == Value ? null : Copy();
|
||||
}
|
||||
|
||||
[Obsolete("Use SerializationManager.PushComposition()")]
|
||||
public override ValueDataNode PushInheritance(ValueDataNode node)
|
||||
{
|
||||
return Copy();
|
||||
|
||||
@@ -11,30 +11,25 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
/// <summary>
|
||||
/// A custom type serializer for reading a set of types that inherit from some base type.
|
||||
/// A custom type serializer for reading a set of types that inherit from some base type.
|
||||
/// </summary>
|
||||
public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dictionary<Type, TValue>, MappingDataNode>
|
||||
public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dictionary<Type, TValue>, MappingDataNode>
|
||||
where TValue : notnull
|
||||
{
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||
{
|
||||
{
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
foreach (var (keyNode, valueNode) in node.Children)
|
||||
foreach (var (key, valueNode) in node.Children)
|
||||
{
|
||||
if (keyNode is not ValueDataNode key)
|
||||
{
|
||||
mapping.Add(new ErrorNode(keyNode, $"Expected {nameof(ValueDataNode)} but was {keyNode.GetType()}"), new ValidatedValueNode(valueNode));
|
||||
continue;
|
||||
}
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key.Value);
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key);
|
||||
if (type == null)
|
||||
{
|
||||
mapping.Add(new ErrorNode(keyNode, $"Could not resolve type: {key.Value}"), new ValidatedValueNode(valueNode));
|
||||
mapping.Add(new ErrorNode(node.GetKeyNode(key), $"Could not resolve type: {key}"), new ValidatedValueNode(valueNode));
|
||||
continue;
|
||||
}
|
||||
|
||||
mapping.Add(new ValidatedValueNode(key), serializationManager.ValidateNode(type, valueNode, context));
|
||||
|
||||
mapping.Add(new ValidatedValueNode(node.GetKeyNode(key)), serializationManager.ValidateNode(type, valueNode, context));
|
||||
}
|
||||
|
||||
return new ValidatedMappingNode(mapping);
|
||||
@@ -44,10 +39,9 @@ public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dicti
|
||||
SerializationHookContext hookCtx, ISerializationContext? context = null, ISerializationManager.InstantiationDelegate<Dictionary<Type, TValue>>? instanceProvider = null)
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<Type, TValue>();
|
||||
foreach (var (keyNode, valueNode) in node.Children)
|
||||
foreach (var (key, valueNode) in node.Children)
|
||||
{
|
||||
var key = (ValueDataNode) keyNode;
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key.Value)!;
|
||||
var type = serializationManager.ReflectionManager.YamlTypeTagLookup(typeof(TValue), key)!;
|
||||
var value = (TValue) serializationManager.Read(type, valueNode, hookCtx, context, notNullableOverride:true)!;
|
||||
dict.Add(type, value);
|
||||
}
|
||||
@@ -62,8 +56,14 @@ public sealed class AbstractDictionarySerializer<TValue> : ITypeSerializer<Dicti
|
||||
|
||||
foreach (var (key, val) in value)
|
||||
{
|
||||
// TODO SERIALIZATION
|
||||
// Add some way to directly return a string w/o allocating a ValueDataNode
|
||||
var keyNode = serializationManager.WriteValue(key.Name, alwaysWrite, context, notNullableOverride: true);
|
||||
if (keyNode is not ValueDataNode valueNode)
|
||||
throw new NotSupportedException();
|
||||
|
||||
mappingNode.Add(
|
||||
serializationManager.WriteValue(key.Name, alwaysWrite, context, notNullableOverride:true),
|
||||
valueNode.Value,
|
||||
serializationManager.WriteValue(key, val, alwaysWrite, context));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,13 +32,8 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Pro
|
||||
|
||||
foreach (var (key, val) in node.Children)
|
||||
{
|
||||
if (key is not ValueDataNode value)
|
||||
{
|
||||
mapping.Add(new ErrorNode(key, $"Cannot cast node {key} to ValueDataNode."), serializationManager.ValidateNode<TValue>(val, context));
|
||||
continue;
|
||||
}
|
||||
|
||||
mapping.Add(PrototypeSerializer.Validate(serializationManager, value, dependencies, context), serializationManager.ValidateNode<TValue>(val, context));
|
||||
var keyNode = new ValueDataNode(key);
|
||||
mapping.Add(PrototypeSerializer.Validate(serializationManager, keyNode, dependencies, context), serializationManager.ValidateNode<TValue>(val, context));
|
||||
}
|
||||
|
||||
return new ValidatedMappingNode(mapping);
|
||||
|
||||
@@ -30,8 +30,9 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Pro
|
||||
{
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
|
||||
foreach (var (key, val) in node.Children)
|
||||
foreach (var (k, val) in node.Children)
|
||||
{
|
||||
var key = node.GetKeyNode(k);
|
||||
if (val is not ValueDataNode value)
|
||||
{
|
||||
mapping.Add(new ErrorNode(val, $"Cannot cast node {val} to ValueDataNode."), serializationManager.ValidateNode<TValue>(key, context));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -8,6 +9,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||
@@ -59,7 +61,7 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
var mapping = new Dictionary<ValidationNode, ValidationNode>();
|
||||
foreach (var (key, val) in node.Children)
|
||||
{
|
||||
mapping.Add(serializationManager.ValidateNode<TKey>(key, context),
|
||||
mapping.Add(serializationManager.ValidateNode<TKey>(node.GetKeyNode(key), context),
|
||||
serializationManager.ValidateNode<TValue>(val, context));
|
||||
}
|
||||
|
||||
@@ -79,8 +81,14 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
var mappingNode = new MappingDataNode();
|
||||
foreach (var (key, val) in value)
|
||||
{
|
||||
// TODO SERIALIZATION
|
||||
// Add some way to directly return a string w/o allocating a ValueDataNode
|
||||
var keyNode = serializationManager.WriteValue(key, alwaysWrite, context);
|
||||
if (keyNode is not ValueDataNode valueNode)
|
||||
throw new NotSupportedException("Yaml mapping keys must serialize to a ValueDataNode (i.e. a string)");
|
||||
|
||||
mappingNode.Add(
|
||||
serializationManager.WriteValue(key, alwaysWrite, context),
|
||||
valueNode.Value,
|
||||
serializationManager.WriteValue(val, alwaysWrite, context));
|
||||
}
|
||||
|
||||
@@ -128,9 +136,11 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<TKey, TValue>();
|
||||
|
||||
var keyNode = new ValueDataNode();
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
|
||||
keyNode.Value = key;
|
||||
dict.Add(serializationManager.Read<TKey>(keyNode, hookCtx, context),
|
||||
serializationManager.Read<TValue>(value, hookCtx, context));
|
||||
}
|
||||
|
||||
@@ -149,9 +159,11 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
|
||||
var array = new KeyValuePair<TKey, TValue>[node.Children.Count];
|
||||
int i = 0;
|
||||
var keyNode = new ValueDataNode();
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
var k = serializationManager.Read<TKey>(key, hookCtx, context);
|
||||
keyNode.Value = key;
|
||||
var k = serializationManager.Read<TKey>(keyNode, hookCtx, context);
|
||||
var v = serializationManager.Read<TValue>(value, hookCtx, context);
|
||||
array[i++] = new(k,v);
|
||||
}
|
||||
@@ -174,9 +186,11 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
|
||||
var dict = new Dictionary<TKey, TValue>();
|
||||
|
||||
var keyNode = new ValueDataNode();
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
|
||||
keyNode.Value = key;
|
||||
dict.Add(serializationManager.Read<TKey>(keyNode, hookCtx, context),
|
||||
serializationManager.Read<TValue>(value, hookCtx, context));
|
||||
}
|
||||
|
||||
@@ -190,10 +204,12 @@ public sealed class DictionarySerializer<TKey, TValue> :
|
||||
ISerializationManager.InstantiationDelegate<SortedDictionary<TKey, TValue>>? instanceProvider)
|
||||
{
|
||||
var dict = instanceProvider != null ? instanceProvider() : new SortedDictionary<TKey, TValue>();
|
||||
var keyNode = new ValueDataNode();
|
||||
|
||||
foreach (var (key, value) in node.Children)
|
||||
{
|
||||
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
|
||||
keyNode.Value = key;
|
||||
dict.Add(serializationManager.Read<TKey>(keyNode, hookCtx, context),
|
||||
serializationManager.Read<TValue>(value, hookCtx, context));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,40 +6,67 @@ using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
|
||||
{
|
||||
[TypeSerializer]
|
||||
public sealed class ValueTupleSerializer<T1, T2> : ITypeSerializer<ValueTuple<T1, T2>, MappingDataNode>, ITypeCopyCreator<ValueTuple<T1, T2>>
|
||||
public sealed class ValueTupleSerializer<T1, T2> :
|
||||
ITypeReader<ValueTuple<T1, T2>, MappingDataNode>,
|
||||
ITypeSerializer<ValueTuple<T1, T2>, SequenceDataNode>,
|
||||
ITypeCopyCreator<ValueTuple<T1, T2>>
|
||||
{
|
||||
public (T1, T2) Read(ISerializationManager serializationManager, MappingDataNode node,
|
||||
public (T1, T2) Read(
|
||||
ISerializationManager serializationManager,
|
||||
MappingDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null, ISerializationManager.InstantiationDelegate<(T1, T2)>? val = null)
|
||||
ISerializationContext? context = null,
|
||||
ISerializationManager.InstantiationDelegate<(T1, T2)>? instanceProvider = null)
|
||||
{
|
||||
if (node.Children.Count != 1)
|
||||
throw new InvalidMappingException("Less than or more than 1 mappings provided to ValueTupleSerializer");
|
||||
|
||||
var entry = node.Children.First();
|
||||
var v1 = serializationManager.Read<T1>(entry.Key, hookCtx, context);
|
||||
var v1 = serializationManager.Read<T1>(node.GetKeyNode(entry.Key), hookCtx, context);
|
||||
var v2 = serializationManager.Read<T2>(entry.Value, hookCtx, context);
|
||||
|
||||
return (v1, v2);
|
||||
}
|
||||
|
||||
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||
public (T1, T2) Read(
|
||||
ISerializationManager serializationManager,
|
||||
SequenceDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null,
|
||||
ISerializationManager.InstantiationDelegate<(T1, T2)>? val = null)
|
||||
{
|
||||
if (node.Count != 2)
|
||||
throw new InvalidMappingException("Sequence must contain exactly 2 elements.");
|
||||
|
||||
var v1 = serializationManager.Read<T1>(node[0], hookCtx, context);
|
||||
var v2 = serializationManager.Read<T2>(node[1], hookCtx, context);
|
||||
|
||||
return (v1, v2);
|
||||
}
|
||||
|
||||
public ValidationNode Validate(
|
||||
ISerializationManager serializationManager,
|
||||
MappingDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
if (node.Children.Count != 1) return new ErrorNode(node, "More or less than 1 Mapping for ValueTuple found.");
|
||||
if (node.Children.Count != 1)
|
||||
return new ErrorNode(node, "More or less than 1 Mapping for ValueTuple found.");
|
||||
|
||||
var entry = node.Children.First();
|
||||
var dict = new Dictionary<ValidationNode, ValidationNode>
|
||||
{
|
||||
{
|
||||
serializationManager.ValidateNode<T1>(entry.Key, context),
|
||||
serializationManager.ValidateNode<T1>(node.GetKeyNode(entry.Key), context),
|
||||
serializationManager.ValidateNode<T2>(entry.Value, context)
|
||||
}
|
||||
};
|
||||
@@ -47,21 +74,44 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
|
||||
return new ValidatedMappingNode(dict);
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, (T1, T2) value,
|
||||
IDependencyCollection dependencies, bool alwaysWrite = false,
|
||||
public ValidationNode Validate(
|
||||
ISerializationManager serializationManager,
|
||||
SequenceDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
var mapping = new MappingDataNode();
|
||||
if (node.Count != 2)
|
||||
throw new InvalidMappingException("Sequence must contain exactly 2 elements.");
|
||||
|
||||
mapping.Add(
|
||||
serializationManager.WriteValue<T1>(value.Item1, alwaysWrite, context),
|
||||
serializationManager.WriteValue<T2>(value.Item2, alwaysWrite, context));
|
||||
var seq = new List<ValidationNode>
|
||||
{
|
||||
serializationManager.ValidateNode<T1>(node[0], context),
|
||||
serializationManager.ValidateNode<T2>(node[1], context)
|
||||
};
|
||||
|
||||
return mapping;
|
||||
return new ValidatedSequenceNode(seq);
|
||||
}
|
||||
|
||||
public (T1, T2) CreateCopy(ISerializationManager serializationManager, (T1, T2) source,
|
||||
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
|
||||
public DataNode Write(
|
||||
ISerializationManager serializationManager,
|
||||
(T1, T2) value,
|
||||
IDependencyCollection dependencies,
|
||||
bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return new SequenceDataNode(new List<DataNode>
|
||||
{
|
||||
serializationManager.WriteValue(value.Item1, alwaysWrite, context),
|
||||
serializationManager.WriteValue(value.Item2, alwaysWrite, context)
|
||||
});
|
||||
}
|
||||
|
||||
public (T1, T2) CreateCopy(
|
||||
ISerializationManager serializationManager,
|
||||
(T1, T2) source,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
return (serializationManager.CreateCopy(source.Item1, hookCtx, context),
|
||||
serializationManager.CreateCopy(source.Item2, hookCtx, context));
|
||||
|
||||
@@ -69,10 +69,10 @@ public sealed partial class ManagerTests : SerializationTest
|
||||
}, //ISelfSerialize
|
||||
new object[]
|
||||
{
|
||||
new MappingDataNode(new Dictionary<DataNode, DataNode>
|
||||
new MappingDataNode(new Dictionary<string, DataNode>
|
||||
{
|
||||
{ new ValueDataNode("one"), new ValueDataNode("valueOne") },
|
||||
{ new ValueDataNode("two"), new SequenceDataNode("2", "3") },
|
||||
{ "one", new ValueDataNode("valueOne") },
|
||||
{ "two", new SequenceDataNode("2", "3") },
|
||||
}){Tag = $"!type:{nameof(DataDefClass)}"},
|
||||
() => (IDataDefBaseInterface)new DataDefClass
|
||||
{
|
||||
@@ -112,10 +112,10 @@ public sealed partial class ManagerTests : SerializationTest
|
||||
}, //array
|
||||
new object[]
|
||||
{
|
||||
new MappingDataNode(new Dictionary<DataNode, DataNode>
|
||||
new MappingDataNode(new Dictionary<string, DataNode>
|
||||
{
|
||||
{ new ValueDataNode("one"), new ValueDataNode("valueOne") },
|
||||
{ new ValueDataNode("two"), new SequenceDataNode("2", "3") },
|
||||
{ "one", new ValueDataNode("valueOne") },
|
||||
{ "two", new SequenceDataNode("2", "3") },
|
||||
}),
|
||||
() => new DataDefClass
|
||||
{
|
||||
@@ -204,10 +204,10 @@ public sealed partial class ManagerTests : SerializationTest
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new MappingDataNode(new Dictionary<DataNode, DataNode>()
|
||||
new MappingDataNode(new Dictionary<string, DataNode>()
|
||||
{
|
||||
{ new ValueDataNode("one"), new ValueDataNode("valueOne") },
|
||||
{ new ValueDataNode("two"), new SequenceDataNode("2", "3") },
|
||||
{ "one", new ValueDataNode("valueOne") },
|
||||
{ "two", new SequenceDataNode("2", "3") },
|
||||
}),
|
||||
() => new DataDefStruct
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ public sealed partial class ReadValueProviderTests : SerializationTest
|
||||
public void DataDefinitionMappingBaseTest()
|
||||
{
|
||||
var data = "someData";
|
||||
var mapping = new MappingDataNode(new Dictionary<DataNode, DataNode>{{ new ValueDataNode("data"), new ValueDataNode(data) }})
|
||||
var mapping = new MappingDataNode(new Dictionary<string, DataNode>{{ "data", new ValueDataNode(data) }})
|
||||
{
|
||||
Tag = $"!type:{nameof(DataDefinitionValueProviderTestDummy)}"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user