Skip to content

Commit

Permalink
Add support for dec refs for values of dictionaries that are input wi…
Browse files Browse the repository at this point in the history
…th non-`li` keys.
  • Loading branch information
zorbathut committed Nov 7, 2024
1 parent 9da4324 commit b2fbcea
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 11 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file.
* SetupDependsOnAttribute, which allows you to define ConfigErrors/PostLoad dependencies between Dec classes. This is a prototype and will probably change in the future.
* A general-purpose stable dag evaluator, which is exposed mostly because it's often convenient.
* Recorder.InputContext, which gives contextual diagnostic information useful for reporting Recorder issues.
* The ability to reference class objects contained within Decs. Right now the path generation works only for members, lists, and arrays; this will probably be improved later.
* The ability to reference class objects contained within Decs. Right now the path generation works only for members, lists, arrays, and values of dictionaries that are input with non-`li` keys; this will probably be improved later.
* Debug-log functions to Context and Recorder, making it easier to report issues.

### Breaking
Expand Down
28 changes: 25 additions & 3 deletions src/Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,28 @@ public override bool IsValidForWriting()
}
}

public class PathDictionaryValue : Path
{
private Path parent;
private string key;

public PathDictionaryValue(Path parent, string key)
{
this.parent = parent;
this.key = key;
}

public override string Serialize()
{
return $"{parent.Serialize()}[{key}]";
}

public override bool IsValidForWriting()
{
return parent.IsValidForWriting();
}
}

// the ones after this point are grossly incomplete

public class PathDictionaryKey : Path
Expand All @@ -169,18 +191,18 @@ public override bool IsValidForWriting()
}
}

public class PathDictionaryValue : Path
public class PathDictionaryValueUnpathable : Path
{
private Path parent;

public PathDictionaryValue(Path parent)
public PathDictionaryValueUnpathable(Path parent)
{
this.parent = parent;
}

public override string Serialize()
{
return $"{parent.Serialize()}[nyi]";
return $"{parent.Serialize()}[UNSERIALIZABLE]";
}

public override bool IsValidForWriting()
Expand Down
14 changes: 12 additions & 2 deletions src/ReaderXml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public override void ParseDictionary(IDictionary dict, Type referencedKeyType, T

writtenFields?.Add(key);

var valuePath = new PathDictionaryValue(path);
var valuePath = new PathDictionaryValueUnpathable(path);
dict[key] = Serialization.ParseElement(new List<ReaderNodeParseable>() { new ReaderNodeXml(valueNode, fileIdentifier,valuePath, UserSettings) }, referencedValueType, originalValue, readerGlobals, recorderChildContext);
}
else
Expand Down Expand Up @@ -285,7 +285,17 @@ public override void ParseDictionary(IDictionary dict, Type referencedKeyType, T

writtenFields?.Add(key);

var valuePath = new PathDictionaryValue(path);
Path valuePath;
if (fieldElement.Name.LocalName.Contains('[') || fieldElement.Name.LocalName.Contains(']'))
{
// I'm not actually sure this is possible
valuePath = new PathDictionaryValueUnpathable(path);
}
else
{
valuePath = new PathDictionaryValue(path, fieldElement.Name.LocalName);
}

dict[key] = Serialization.ParseElement(new List<ReaderNodeParseable>() { new ReaderNodeXml(fieldElement, fileIdentifier, valuePath, UserSettings) }, valueType, originalValue, readerGlobals, recorderChildContext);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/WriterValidation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ public override void WriteDictionary(IDictionary value)
Serialization.ComposeElement(keyNode, iterator.Key, keyType);

writer.AppendLine($"if ({accessor}.ContainsKey({keyNode.SerializedString})) {{");
Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{keyNode.SerializedString}]", new PathDictionaryValue(Path)), iterator.Value, valueType);
Serialization.ComposeElement(new WriterNodeValidation(writer, $"{accessor}[{keyNode.SerializedString}]", new PathDictionaryValueUnpathable(Path)), iterator.Value, valueType);
writer.AppendLine($"}} else {{");
writer.AppendLine($"Assert.IsTrue({accessor}.ContainsKey({keyNode.SerializedString}));"); // this is unnecessary - it could just be .Fail() - but this gives you a *much* better error message
writer.AppendLine($"}}");
Expand Down
4 changes: 3 additions & 1 deletion src/WriterXml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,9 @@ public override void WriteDictionary(IDictionary value)
var li = CreateNamedChild("li", RecorderSettings, Path);

Serialization.ComposeElement(li.CreateNamedChild("key", RecorderSettings.CreateChild(), new PathDictionaryKey(Path)), iterator.Key, keyType);
Serialization.ComposeElement(li.CreateNamedChild("value", RecorderSettings.CreateChild(), new PathDictionaryValue(Path)), iterator.Value, valueType);

// unfortunate consequence: we can't generate sensible Paths when doing this
Serialization.ComposeElement(li.CreateNamedChild("value", RecorderSettings.CreateChild(), new PathDictionaryValueUnpathable(Path)), iterator.Value, valueType);
}
}

Expand Down
11 changes: 8 additions & 3 deletions test/unit/Path.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,23 @@ public void DictionaryValuePath([ValuesExcept(ParserMode.Validation)] ParserMode
<dictValue>
<li>
<key>first</key>
<value>PathDec.TestDec.dictValue[nyi]</value>
<value>PathDec.TestDec.dictValue[UNSERIALIZABLE]</value>
</li>
<second>PathDec.TestDec.dictValue[second]</second>
</dictValue>
</PathDec>
</Decs>");
parser.Finish();

Assert.IsTrue(PathTester.validations == 1);
Assert.IsTrue(PathTester.validations == 2);

// the `second` currently does not serialize properly, so we replace the value there for now
// fix this when we add support!
Dec.Database<PathDec>.Get("TestDec").dictValue["second"] = new PathTester { text = "PathDec.TestDec.dictValue[UNSERIALIZABLE]" };

DoParserTests(mode);

Assert.IsTrue(PathTester.validations == (mode == ParserMode.Bare ? 1 : 3));
Assert.IsTrue(PathTester.validations == (mode == ParserMode.Bare ? 2 : 6));
}

[Test]
Expand Down
17 changes: 17 additions & 0 deletions test/unit/PathDecRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ private class PathDec : Dec.Dec
public StubRecordable[] array;
public StubRecordable[,] arrayMulti;
public List<StubRecordable> list;
public Dictionary<string, StubRecordable> dictionary;
}

[SetUp]
Expand Down Expand Up @@ -44,6 +45,10 @@ public void Setup()
<li />
<li />
</list>
<dictionary>
<horse />
<dog />
</dictionary>
</PathDec>
</Decs>");
parser.Finish();
Expand Down Expand Up @@ -96,5 +101,17 @@ public void List([ValuesExcept(RecorderMode.Simple)] RecorderMode mode, [Values(

Assert.AreSame(item, newItem);
}

[Test]
public void Dictionary([ValuesExcept(RecorderMode.Simple)] RecorderMode mode, [Values("horse", "dog")] string key)
{
var dec = Dec.Database<PathDec>.Get("TestDec");

var item = dec.dictionary[key];

var newItem = DoRecorderRoundTrip(item, mode);

Assert.AreSame(item, newItem);
}
}
}

0 comments on commit b2fbcea

Please sign in to comment.