diff --git a/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj b/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj index 552ed73..e127dbd 100644 --- a/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj +++ b/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj @@ -4,8 +4,8 @@ ModernUO.Serialization.Annotations netstandard2.0 preview - 4.2.0 - 4.2.0 + 4.3.0 + 4.3.0 ModernUO.Serialization.Annotations ModernUO.Serialization true diff --git a/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs b/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs index faa2c75..eb3893f 100644 --- a/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs +++ b/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs @@ -7,14 +7,19 @@ namespace ModernUO.Serialization.Generator.Tests; /// /// Runs the generated AddToX/RemoveFromX/InsertIntoX/ReplaceInX/ClearX helpers against the /// Snapshots/NullableCollections entity: removals and clears tolerate a null collection, adds create -/// it lazily (keeping a [SortedSetComparer]), and MarkDirty fires only when the collection changed. +/// it lazily (keeping a [SortedSetComparer]), MarkDirty fires only when the collection changed, and +/// helpers that can be a no-op return whether they changed anything. /// public class DataStructureMethodTests { - private static string FixtureSource([CallerFilePath] string thisFile = "") => - File.ReadAllText( - Path.Combine(Path.GetDirectoryName(thisFile)!, "Snapshots", "NullableCollections", "Input.cs") - ); + private static string FixtureSource(string snapshot, [CallerFilePath] string thisFile = "") => + File.ReadAllText(Path.Combine(Path.GetDirectoryName(thisFile)!, "Snapshots", snapshot, "Input.cs")); + + private static string Run(string assemblyName, string source) => + (string)SourceGeneratorTestHelper.CompileAndLoad(assemblyName, source) + .GetType("Server.TestContent.Driver")! + .GetMethod("Run")! + .Invoke(null, null)!; private const string Driver = """ namespace Server.TestContent @@ -28,42 +33,37 @@ public static string Run() void Step(string name, object? state = null) => log.Append(name).Append('=').Append(item.DirtyCount).Append(state != null ? $":{state}" : "").Append(';'); - item.RemoveFromCharges(1); + var removed = item.RemoveFromCharges(1) | item.RemoveFromKeywords("a") | + item.RemoveFromLabels(1) | item.RemoveFromNames("a"); item.RemoveFromChargesAt(0); item.ClearCharges(); - item.RemoveFromKeywords("a"); item.ClearKeywords(); - item.RemoveFromLabels(1); item.ClearLabels(); - item.RemoveFromNames("a"); item.ClearNames(); - Step("null", item.Charges == null && item.Keywords == null && item.Labels == null && item.Names == null); + Step("null", $"{removed}:{item.Charges == null && item.Keywords == null && item.Labels == null && item.Names == null}"); item.AddToCharges(5); Step("addList"); item.InsertIntoCharges(0, 4); Step("insert", string.Join(",", item.Charges!)); - item.AddToKeywords("a"); - Step("addSet"); - item.AddToKeywords("a"); - Step("addSetDuplicate"); + Step("addSet", item.AddToKeywords("a")); + Step("addSetDuplicate", item.AddToKeywords("a")); - item.AddToLabels(1, "x"); - Step("addDictionary"); - item.AddToLabels(1, "y"); - Step("addDictionaryDuplicate", item.Labels![1]); + Step("addDictionary", item.AddToLabels(1, "x")); + Step("addDictionaryDuplicate", $"{item.AddToLabels(1, "y")}:{item.Labels![1]}"); item.ReplaceInLabels(1, null); Step("replace", item.Labels[1] ?? "null"); item.AddToNames("Bob"); - item.AddToNames("BOB"); - Step("comparer", item.Names!.Count); + Step("comparer", $"{item.AddToNames("BOB")}:{item.Names!.Count}"); - item.RemoveFromKeywords("missing"); - Step("removeMissing"); - item.RemoveFromKeywords("a"); - Step("remove"); + Step("removeMissing", item.RemoveFromKeywords("missing")); + Step("remove", item.RemoveFromKeywords("a")); + Step("removeListMissing", item.RemoveFromCharges(9)); + Step("removeList", item.RemoveFromCharges(4)); + Step("removeDictionaryMissing", item.RemoveFromLabels(9)); + Step("removeDictionary", item.RemoveFromLabels(1)); item.ClearKeywords(); Step("clearEmpty"); item.ClearCharges(); @@ -79,20 +79,61 @@ void Step(string name, object? state = null) => } """; + // Without a dirty target the helpers still report whether they changed anything. + private const string UntrackedSource = """ + using System.Collections.Generic; + using ModernUO.Serialization; + + namespace Server.TestContent + { + [SerializationGenerator(0)] + public partial class UntrackedRecord + { + [SerializableField(0)] + private List _values = new(); + + [SerializableField(1)] + private HashSet _set = new(); + + [SerializableField(2)] + private Dictionary _lookup = new(); + } + + public static class Driver + { + public static string Run() + { + var r = new UntrackedRecord(); + r.AddToValues(1); + return string.Join( + ",", + r.RemoveFromValues(2), r.RemoveFromValues(1), + r.AddToSet(1), r.AddToSet(1), r.RemoveFromSet(2), r.RemoveFromSet(1), + r.AddToLookup(1, "a"), r.AddToLookup(1, "b"), r.RemoveFromLookup(2), r.RemoveFromLookup(1) + ); + } + } + } + """; + [Fact] public void Helpers_TolerateNullAndMarkDirtyOnlyOnChange() { - var assembly = SourceGeneratorTestHelper.CompileAndLoad("NullableCollections", FixtureSource() + Driver); - - var result = (string)assembly.GetType("Server.TestContent.Driver")! - .GetMethod("Run")! - .Invoke(null, null)!; + Assert.Equal( + "null=0:False:True;addList=1;insert=2:4,5;addSet=3:True;addSetDuplicate=3:False;addDictionary=4:True;" + + "addDictionaryDuplicate=4:False:x;replace=5:null;comparer=6:False:1;removeMissing=6:False;remove=7:True;" + + "removeListMissing=7:False;removeList=8:True;removeDictionaryMissing=8:False;removeDictionary=9:True;" + + "clearEmpty=9;clear=10:0;replaceNull=1:z", + Run("NullableCollections", FixtureSource("NullableCollections") + Driver) + ); + } + [Fact] + public void Helpers_ReportChangesWithoutADirtyTarget() + { Assert.Equal( - "null=0:True;addList=1;insert=2:4,5;addSet=3;addSetDuplicate=3;addDictionary=4;" + - "addDictionaryDuplicate=4:x;replace=5:null;comparer=6:1;removeMissing=6;remove=7;" + - "clearEmpty=7;clear=8:0;replaceNull=1:z", - result + "False,True,True,False,False,True,True,False,False,True", + Run("UntrackedRecord", UntrackedSource) ); } } diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs index d239fea..20db23d 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs @@ -55,12 +55,15 @@ public void AddToCharges(int value) Server.ISerializableExtensions.MarkDirty(this); } - public void RemoveFromCharges(int value) + public bool RemoveFromCharges(int value) { if (_charges?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void InsertIntoCharges(int index, int value) @@ -101,21 +104,27 @@ public System.Collections.Generic.HashSet Keywords } } - public void AddToKeywords(string value) + public bool AddToKeywords(string value) { _keywords ??= new System.Collections.Generic.HashSet(); if (_keywords.Add(value)) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } - public void RemoveFromKeywords(string value) + public bool RemoveFromKeywords(string value) { if (_keywords?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void ClearKeywords() @@ -140,21 +149,27 @@ public System.Collections.Generic.Dictionary Labels } } - public void AddToLabels(int key, string value) + public bool AddToLabels(int key, string value) { _labels ??= new System.Collections.Generic.Dictionary(); if (_labels.TryAdd(key, value)) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } - public void RemoveFromLabels(int key) + public bool RemoveFromLabels(int key) { if (_labels?.Remove(key) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void ReplaceInLabels(int key, string value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs index d5f7a73..210c0f4 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs @@ -41,21 +41,27 @@ public System.Collections.Generic.Dictionary Entries } } - public void AddToEntries(int key, string value) + public bool AddToEntries(int key, string value) { _entries ??= new System.Collections.Generic.Dictionary(); if (_entries.TryAdd(key, value)) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } - public void RemoveFromEntries(int key) + public bool RemoveFromEntries(int key) { if (_entries?.Remove(key) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void ReplaceInEntries(int key, string value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs index 4be42ac..8e3b85d 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs @@ -35,12 +35,15 @@ public void AddToExtendedEntries(Server.TestContent.DerivedEntry value) Server.ISerializableExtensions.MarkDirty(this); } - public void RemoveFromExtendedEntries(Server.TestContent.DerivedEntry value) + public bool RemoveFromExtendedEntries(Server.TestContent.DerivedEntry value) { if (_extendedEntries?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void InsertIntoExtendedEntries(int index, Server.TestContent.DerivedEntry value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs index c168f12..631ca1e 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs @@ -35,12 +35,15 @@ public void AddToEntries(Server.TestContent.BaseEntry value) Server.ISerializableExtensions.MarkDirty(this); } - public void RemoveFromEntries(Server.TestContent.BaseEntry value) + public bool RemoveFromEntries(Server.TestContent.BaseEntry value) { if (_entries?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void InsertIntoEntries(int index, Server.TestContent.BaseEntry value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs index 1c9da54..232aca7 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs @@ -45,9 +45,9 @@ public void AddToValues(int value) _values.Add(value); } - public void RemoveFromValues(int value) + public bool RemoveFromValues(int value) { - _values?.Remove(value); + return _values?.Remove(value) == true; } public void InsertIntoValues(int index, int value) @@ -84,15 +84,15 @@ public System.Collections.Generic.Dictionary Lookup } } - public void AddToLookup(int key, string value) + public bool AddToLookup(int key, string value) { _lookup ??= new System.Collections.Generic.Dictionary(); - _lookup.TryAdd(key, value); + return _lookup.TryAdd(key, value); } - public void RemoveFromLookup(int key) + public bool RemoveFromLookup(int key) { - _lookup?.Remove(key); + return _lookup?.Remove(key) == true; } public void ReplaceInLookup(int key, string value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs index fae98bc..278b34f 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs @@ -48,12 +48,15 @@ public void AddToCharges(int value) MarkDirty(); } - public void RemoveFromCharges(int value) + public bool RemoveFromCharges(int value) { if (_charges?.Remove(value) == true) { MarkDirty(); + return true; } + + return false; } public void InsertIntoCharges(int index, int value) @@ -94,21 +97,27 @@ public System.Collections.Generic.HashSet? Keywords } } - public void AddToKeywords(string value) + public bool AddToKeywords(string value) { _keywords ??= new System.Collections.Generic.HashSet(); if (_keywords.Add(value)) { MarkDirty(); + return true; } + + return false; } - public void RemoveFromKeywords(string value) + public bool RemoveFromKeywords(string value) { if (_keywords?.Remove(value) == true) { MarkDirty(); + return true; } + + return false; } public void ClearKeywords() @@ -133,21 +142,27 @@ public void ClearKeywords() } } - public void AddToLabels(int key, string? value) + public bool AddToLabels(int key, string? value) { _labels ??= new System.Collections.Generic.Dictionary(); if (_labels.TryAdd(key, value)) { MarkDirty(); + return true; } + + return false; } - public void RemoveFromLabels(int key) + public bool RemoveFromLabels(int key) { if (_labels?.Remove(key) == true) { MarkDirty(); + return true; } + + return false; } public void ReplaceInLabels(int key, string? value) @@ -179,21 +194,27 @@ public System.Collections.Generic.SortedSet? Names } } - public void AddToNames(string value) + public bool AddToNames(string value) { _names ??= new System.Collections.Generic.SortedSet(new Server.TestContent.CaseInsensitiveComparer()); if (_names.Add(value)) { MarkDirty(); + return true; } + + return false; } - public void RemoveFromNames(string value) + public bool RemoveFromNames(string value) { if (_names?.Remove(value) == true) { MarkDirty(); + return true; } + + return false; } public void ClearNames() diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs index 4764128..d6e5800 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs @@ -113,12 +113,15 @@ public void AddToSamples(int? value) Server.ISerializableExtensions.MarkDirty(this); } - public void RemoveFromSamples(int? value) + public bool RemoveFromSamples(int? value) { if (_samples?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void InsertIntoSamples(int index, int? value) @@ -159,21 +162,27 @@ public void ClearSamples() } } - public void AddToWeights(int key, double? value) + public bool AddToWeights(int key, double? value) { _weights ??= new System.Collections.Generic.Dictionary(); if (_weights.TryAdd(key, value)) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } - public void RemoveFromWeights(int key) + public bool RemoveFromWeights(int key) { if (_weights?.Remove(key) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void ReplaceInWeights(int key, double? value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs index 5e4940d..8b06cfe 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs @@ -35,12 +35,15 @@ public void AddToEntries(Server.TestContent.RosterEntry value) Server.ISerializableExtensions.MarkDirty(this); } - public void RemoveFromEntries(Server.TestContent.RosterEntry value) + public bool RemoveFromEntries(Server.TestContent.RosterEntry value) { if (_entries?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void InsertIntoEntries(int index, Server.TestContent.RosterEntry value) @@ -81,21 +84,27 @@ public void ClearEntries() } } - public void AddToById(int key, Server.TestContent.RosterEntry value) + public bool AddToById(int key, Server.TestContent.RosterEntry value) { _byId ??= new System.Collections.Generic.Dictionary(); if (_byId.TryAdd(key, value)) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } - public void RemoveFromById(int key) + public bool RemoveFromById(int key) { if (_byId?.Remove(key) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void ReplaceInById(int key, Server.TestContent.RosterEntry value) diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs index 1b77384..b9f0883 100644 --- a/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs +++ b/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs @@ -28,21 +28,27 @@ public System.Collections.Generic.SortedSet Names } } - public void AddToNames(string value) + public bool AddToNames(string value) { _names ??= new System.Collections.Generic.SortedSet(new Server.TestContent.CaseInsensitiveComparer()); if (_names.Add(value)) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } - public void RemoveFromNames(string value) + public bool RemoveFromNames(string value) { if (_names?.Remove(value) == true) { Server.ISerializableExtensions.MarkDirty(this); + return true; } + + return false; } public void ClearNames() diff --git a/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj b/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj index d09c2d1..41b305e 100755 --- a/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj +++ b/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj @@ -4,8 +4,8 @@ ModernUO.Serialization.Generator netstandard2.0 preview - 4.2.0 - 4.2.0 + 4.3.0 + 4.3.0 ModernUO.Serialization.Generator true false diff --git a/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs b/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs index 262086b..1934a42 100644 --- a/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs +++ b/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs @@ -21,21 +21,22 @@ private static void AppendLazyCreate(this StringBuilder source, string indent, F } } - // Marks dirty only when the mutation reports a change. Without a dirty target, just mutates. - private static void AppendIfChanged( - this StringBuilder source, string indent, string changed, string? markDirtyMethod, string? statement = null - ) + // Returns whether the mutation reported a change, marking dirty only when it did. + private static void AppendReturnIfChanged(this StringBuilder source, string indent, string changed, string? markDirtyMethod) { if (markDirtyMethod == null) { - source.AppendLine($"{indent} {statement ?? changed};"); + source.AppendLine($"{indent} return {changed};"); return; } source.AppendLine($"{indent} if ({changed})"); source.AppendLine($"{indent} {{"); source.AppendLine($"{indent} {markDirtyMethod};"); + source.AppendLine($"{indent} return true;"); source.AppendLine($"{indent} }}"); + source.AppendLine(); + source.AppendLine($"{indent} return false;"); } public static bool GenerateDataStructureMethods( @@ -59,34 +60,38 @@ public static bool GenerateDataStructureMethods( // Mutations go through the backing field: lazily creating the collection through the // property setter would run fieldChanged/allowFieldChange for what is not a replacement. // Removals and clears tolerate a null collection, and only mark dirty on an actual change. + // Return types mirror the BCL: a mutation that can be a no-op returns whether it changed anything. if (field.DsIsDictionary) { var valueTypeName = field.DsValueType; // Add - source.AppendLine($"{indent}{propertyAccessor} void AddTo{propertyName}({elementTypeName} key, {valueTypeName} value)"); + source.AppendLine($"{indent}{propertyAccessor} bool AddTo{propertyName}({elementTypeName} key, {valueTypeName} value)"); source.AppendLine($"{indent}{{"); source.AppendLazyCreate(indent, field); if (field.DsHasTryAdd) { - source.AppendIfChanged(indent, $"{fieldName}.TryAdd(key, value)", markDirtyMethod); + source.AppendReturnIfChanged(indent, $"{fieldName}.TryAdd(key, value)", markDirtyMethod); } else { - source.AppendLine($"{indent} if (!{fieldName}.ContainsKey(key))"); + source.AppendLine($"{indent} if ({fieldName}.ContainsKey(key))"); source.AppendLine($"{indent} {{"); - source.AppendLine($"{indent} {fieldName}.Add(key, value);"); - source.AppendMarkDirty($"{indent} ", markDirtyMethod); + source.AppendLine($"{indent} return false;"); source.AppendLine($"{indent} }}"); + source.AppendLine(); + source.AppendLine($"{indent} {fieldName}.Add(key, value);"); + source.AppendMarkDirty(indent, markDirtyMethod); + source.AppendLine($"{indent} return true;"); } source.AppendLine($"{indent}}}"); source.AppendLine(); // Remove - source.AppendLine($"{indent}{propertyAccessor} void RemoveFrom{propertyName}({elementTypeName} key)"); + source.AppendLine($"{indent}{propertyAccessor} bool RemoveFrom{propertyName}({elementTypeName} key)"); source.AppendLine($"{indent}{{"); - source.AppendIfChanged(indent, $"{fieldName}?.Remove(key) == true", markDirtyMethod, $"{fieldName}?.Remove(key)"); + source.AppendReturnIfChanged(indent, $"{fieldName}?.Remove(key) == true", markDirtyMethod); source.AppendLine($"{indent}}}"); source.AppendLine(); @@ -102,12 +107,13 @@ public static bool GenerateDataStructureMethods( else if (field.DsIsCollection) { // Add - source.AppendLine($"{indent}{propertyAccessor} void AddTo{propertyName}({elementTypeName} value)"); + var addReturnType = field.DsIsSet ? "bool" : "void"; + source.AppendLine($"{indent}{propertyAccessor} {addReturnType} AddTo{propertyName}({elementTypeName} value)"); source.AppendLine($"{indent}{{"); source.AppendLazyCreate(indent, field); if (field.DsIsSet) { - source.AppendIfChanged(indent, $"{fieldName}.Add(value)", markDirtyMethod); + source.AppendReturnIfChanged(indent, $"{fieldName}.Add(value)", markDirtyMethod); } else { @@ -119,9 +125,9 @@ public static bool GenerateDataStructureMethods( source.AppendLine(); // Remove - source.AppendLine($"{indent}{propertyAccessor} void RemoveFrom{propertyName}({elementTypeName} value)"); + source.AppendLine($"{indent}{propertyAccessor} bool RemoveFrom{propertyName}({elementTypeName} value)"); source.AppendLine($"{indent}{{"); - source.AppendIfChanged(indent, $"{fieldName}?.Remove(value) == true", markDirtyMethod, $"{fieldName}?.Remove(value)"); + source.AppendReturnIfChanged(indent, $"{fieldName}?.Remove(value) == true", markDirtyMethod); source.AppendLine($"{indent}}}"); } diff --git a/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj b/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj index e19d7b6..11cc681 100755 --- a/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj +++ b/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj @@ -6,8 +6,8 @@ x64;arm64 preview Exe - 4.2.0 - 4.2.0 + 4.3.0 + 4.3.0 true ModernUOSchemaGenerator true diff --git a/README.md b/README.md index c5783fe..34bc107 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ While it is not the most elegant solution (recommendations and contributions are Add `ModernUO.Serialization.Generator` and `ModernUO.Serialization.Annotations` as package references: ```xml - - + + ``` @@ -128,6 +128,9 @@ They tolerate a null collection: adds create it on first use (a `[SortedSetCompa do nothing. They mark the entity dirty only when the collection actually changed, so adding a duplicate to a set or dictionary, or removing a missing entry, does not. +`RemoveFromX`, and `AddToX` on a set or dictionary, return whether the collection changed, like the BCL's `Remove`, +`HashSet.Add` and `TryAdd`. The rest return `void`. + Nullable reference annotations (`string?`, `List?`) are ignored for serialization. Nullable value types (`int?`, `Point3D?`, a nullable `[SerializationGenerator]` struct, or `List`) are written as a `bool` followed by the value when present.