diff --git a/src/NexusMods.MnemonicDB.Abstractions/Attributes/TupleAttribute.cs b/src/NexusMods.MnemonicDB.Abstractions/Attributes/TupleAttribute.cs index a8502a2..f8023d7 100644 --- a/src/NexusMods.MnemonicDB.Abstractions/Attributes/TupleAttribute.cs +++ b/src/NexusMods.MnemonicDB.Abstractions/Attributes/TupleAttribute.cs @@ -77,3 +77,82 @@ protected override (T1LowLevel, T2LowLevel) ToLowLevel((T1HighLevel, T2HighLevel throw new NotSupportedException("This attribute uses custom serialization, and does not support low-level conversion."); } } + + +public class TupleAttribute : + ScalarAttribute<(T1HighLevel, T2HighLevel, T3HighLevel), (T1LowLevel, T2LowLevel, T3LowLevel)> +{ + private readonly ValueTags _tag1; + private readonly ValueTags _tag2; + private readonly ValueTags _tag3; + + /// + /// Creates a new tuple attribute, the value tags are used to determine the type of each elelement in the tuple. + /// + public TupleAttribute(ValueTags tag1, ValueTags tag2, ValueTags tag3, string ns, string name) : base(ValueTags.Tuple3, ns, name) + { + _tag1 = tag1; + _tag2 = tag2; + _tag3 = tag3; + } + + /// + public override (T1HighLevel, T2HighLevel, T3HighLevel) ReadValue(ReadOnlySpan span, ValueTags tag, RegistryId registryId) + { + if (tag != ValueTags.Tuple3) + throw new ArgumentException($"Expected tag {ValueTags.Tuple3}, but got {tag}"); + + var type1 = span[0]; + var type2 = span[1]; + var type3 = span[2]; + + if (type1 != (byte)_tag1 || type2 != (byte)_tag2 || type3 != (byte)_tag3) + throw new ArgumentException($"Expected tag ({_tag1}, {_tag2}, {_tag3}), but got ({type1}, {type2}, {type3})"); + + var valA = ReadValue(span.SliceFast(3), _tag1, registryId, out var sizeA); + var valB = ReadValue(span.SliceFast(3 + sizeA), _tag2, registryId, out var sizeB); + var valC = ReadValue(span.SliceFast(3 + sizeA + sizeB), _tag3, registryId, out _); + + return FromLowLevel((valA, valB, valC)); + } + + /// + protected virtual (T1HighLevel, T2HighLevel, T3HighLevel) FromLowLevel((T1LowLevel, T2LowLevel, T3LowLevel) value) + { + throw new NotSupportedException("You must override this method to support low-level conversion."); + } + + public override void Remap(Func remapper, Span valueSpan) + { + if (_tag1 == ValueTags.Reference) + { + var entityId = MemoryMarshal.Read(valueSpan.SliceFast(2)); + var newEntityId = remapper(entityId); + MemoryMarshal.Write(valueSpan.SliceFast(2), newEntityId); + } + else if (_tag2 == ValueTags.Reference) + { + throw new NotImplementedException(); + } + } + + /// + public override void WriteValue((T1HighLevel, T2HighLevel, T3HighLevel) value, TWriter writer) + { + var span = writer.GetSpan(3); + span[0] = (byte)_tag1; + span[1] = (byte)_tag2; + span[2] = (byte)_tag3; + writer.Advance(3); + + WriteValueLowLevel(value.Item1, _tag1, writer); + WriteValueLowLevel(value.Item2, _tag2, writer); + WriteValueLowLevel(value.Item3, _tag3, writer); + } + + /// + protected override (T1LowLevel, T2LowLevel, T3LowLevel) ToLowLevel((T1HighLevel, T2HighLevel, T3HighLevel) value) + { + throw new NotSupportedException("This attribute uses custom serialization, and does not support low-level conversion."); + } +} diff --git a/tests/NexusMods.MnemonicDB.TestModel/Attributes/Int3Attribute.cs b/tests/NexusMods.MnemonicDB.TestModel/Attributes/Int3Attribute.cs new file mode 100644 index 0000000..ee7568a --- /dev/null +++ b/tests/NexusMods.MnemonicDB.TestModel/Attributes/Int3Attribute.cs @@ -0,0 +1,18 @@ +using NexusMods.MnemonicDB.Abstractions; +using NexusMods.MnemonicDB.Abstractions.Attributes; +using NexusMods.MnemonicDB.Abstractions.ElementComparers; + +namespace NexusMods.MnemonicDB.TestModel.Attributes; + +public class Int3Attribute(string ns, string name) : TupleAttribute(ValueTags.Int32, ValueTags.Int32, ValueTags.Int32, ns, name) +{ + protected override (int, int, int) FromLowLevel((int, int, int) value) + { + return value; + } + + protected override (int, int, int) ToLowLevel((int, int, int) value) + { + return value; + } +} diff --git a/tests/NexusMods.MnemonicDB.TestModel/File.cs b/tests/NexusMods.MnemonicDB.TestModel/File.cs index 68adea1..8f77e40 100644 --- a/tests/NexusMods.MnemonicDB.TestModel/File.cs +++ b/tests/NexusMods.MnemonicDB.TestModel/File.cs @@ -1,5 +1,6 @@ using NexusMods.Hashing.xxHash64; using NexusMods.MnemonicDB.Abstractions.Attributes; +using NexusMods.MnemonicDB.Abstractions.ElementComparers; using NexusMods.MnemonicDB.Abstractions.Models; using NexusMods.MnemonicDB.TestModel.Attributes; @@ -17,4 +18,9 @@ public partial class File : IModelDefinition /// A combination of the loadout /// public static readonly TuplePath TuplePath = new(Namespace, nameof(TuplePath)) {IsIndexed = true, IsOptional = true}; + + /// + /// Tuple3 test + /// + public static readonly Int3Attribute TupleTest = new(Namespace, nameof(TupleTest)) { IsIndexed = true, IsOptional = true}; } diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.CanGetDatomsFromEntity.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.CanGetDatomsFromEntity.verified.txt index 9cc5daa..285946e 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.CanGetDatomsFromEntity.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.CanGetDatomsFromEntity.verified.txt @@ -1,3 +1,3 @@ -+ | 0200000000000002 | (0010) Name | Mod1 - Updated | 0100000000000004 -+ | 0200000000000002 | (0011) Source | http://somesite.com/Mod1 | 0100000000000003 -+ | 0200000000000002 | (0012) Loadout | 0200000000000001 | 0100000000000003 ++ | 0200000000000002 | (0011) Name | Mod1 - Updated | 0100000000000004 ++ | 0200000000000002 | (0012) Source | http://somesite.com/Mod1 | 0100000000000003 ++ | 0200000000000002 | (0013) Loadout | 0200000000000001 | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesCanBeConstructedSeparately.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesCanBeConstructedSeparately.verified.txt index ad15b94..85ce3fb 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesCanBeConstructedSeparately.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesCanBeConstructedSeparately.verified.txt @@ -1,4 +1,4 @@ + | 0100000000000003 | (0008) Timestamp | DateTime : 0 | 0100000000000003 -+ | 0200000000000001 | (001D) Name | Parent A | 0100000000000003 -+ | 0200000000000001 | (001E) Name | Parent B | 0100000000000003 -+ | 0200000000000001 | (001F) Name | Test Child | 0100000000000003 ++ | 0200000000000001 | (001E) Name | Parent A | 0100000000000003 ++ | 0200000000000001 | (001F) Name | Parent B | 0100000000000003 ++ | 0200000000000001 | (0020) Name | Test Child | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesDontSplitEntities.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesDontSplitEntities.verified.txt index ad15b94..85ce3fb 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesDontSplitEntities.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.MultipleIncludesDontSplitEntities.verified.txt @@ -1,4 +1,4 @@ + | 0100000000000003 | (0008) Timestamp | DateTime : 0 | 0100000000000003 -+ | 0200000000000001 | (001D) Name | Parent A | 0100000000000003 -+ | 0200000000000001 | (001E) Name | Parent B | 0100000000000003 -+ | 0200000000000001 | (001F) Name | Test Child | 0100000000000003 ++ | 0200000000000001 | (001E) Name | Parent A | 0100000000000003 ++ | 0200000000000001 | (001F) Name | Parent B | 0100000000000003 ++ | 0200000000000001 | (0020) Name | Test Child | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927939.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927939.verified.txt index 910aa19..f0e4949 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927939.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927939.verified.txt @@ -1,3 +1,3 @@ -+ | 0200000000000001 | (0010) Name | Test Mod | 0100000000000003 -+ | 0200000000000001 | (0011) Source | http://test.com/ | 0100000000000003 -+ | 0200000000000001 | (0012) Loadout | 0200000000000002 | 0100000000000003 ++ | 0200000000000001 | (0011) Name | Test Mod | 0100000000000003 ++ | 0200000000000001 | (0012) Source | http://test.com/ | 0100000000000003 ++ | 0200000000000001 | (0013) Loadout | 0200000000000002 | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927940.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927940.verified.txt index cccfb95..6cd249f 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927940.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927940.verified.txt @@ -1,3 +1,3 @@ -+ | 0200000000000001 | (0010) Name | Test Mod 0 | 0100000000000004 -+ | 0200000000000001 | (0011) Source | http://test.com/ | 0100000000000003 -+ | 0200000000000001 | (0012) Loadout | 0200000000000002 | 0100000000000003 ++ | 0200000000000001 | (0011) Name | Test Mod 0 | 0100000000000004 ++ | 0200000000000001 | (0012) Source | http://test.com/ | 0100000000000003 ++ | 0200000000000001 | (0013) Loadout | 0200000000000002 | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927941.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927941.verified.txt index 224727a..08fbb46 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927941.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927941.verified.txt @@ -1,3 +1,3 @@ -+ | 0200000000000001 | (0010) Name | Test Mod 1 | 0100000000000005 -+ | 0200000000000001 | (0011) Source | http://test.com/ | 0100000000000003 -+ | 0200000000000001 | (0012) Loadout | 0200000000000002 | 0100000000000003 ++ | 0200000000000001 | (0011) Name | Test Mod 1 | 0100000000000005 ++ | 0200000000000001 | (0012) Source | http://test.com/ | 0100000000000003 ++ | 0200000000000001 | (0013) Loadout | 0200000000000002 | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927942.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927942.verified.txt index 91ecf65..27b1359 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927942.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadDatomsOverTime_mod data_72057594037927942.verified.txt @@ -1,3 +1,3 @@ -+ | 0200000000000001 | (0010) Name | Test Mod 2 | 0100000000000006 -+ | 0200000000000001 | (0011) Source | http://test.com/ | 0100000000000003 -+ | 0200000000000001 | (0012) Loadout | 0200000000000002 | 0100000000000003 ++ | 0200000000000001 | (0011) Name | Test Mod 2 | 0100000000000006 ++ | 0200000000000001 | (0012) Source | http://test.com/ | 0100000000000003 ++ | 0200000000000001 | (0013) Loadout | 0200000000000002 | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_archive file data.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_archive file data.verified.txt index 1e56941..c5ee249 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_archive file data.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_archive file data.verified.txt @@ -2,5 +2,5 @@ + | 0200000000000001 | (000A) Hash | 0x00000000DEADBEF0 | 0100000000000003 + | 0200000000000001 | (000B) Size | 1 B | 0100000000000003 + | 0200000000000001 | (000C) Mod | 0000000000000001 | 0100000000000003 -+ | 0200000000000001 | (000E) Path | C:\test.zip | 0100000000000003 -+ | 0200000000000001 | (000F) Hash | 0x00000000FEEDBEEF | 0100000000000003 ++ | 0200000000000001 | (000F) Path | C:\test.zip | 0100000000000003 ++ | 0200000000000001 | (0010) Hash | 0x00000000FEEDBEEF | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_file data.verified.txt b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_file data.verified.txt index 1e56941..c5ee249 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_file data.verified.txt +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.ReadModelsCanHaveExtraAttributes_file data.verified.txt @@ -2,5 +2,5 @@ + | 0200000000000001 | (000A) Hash | 0x00000000DEADBEF0 | 0100000000000003 + | 0200000000000001 | (000B) Size | 1 B | 0100000000000003 + | 0200000000000001 | (000C) Mod | 0000000000000001 | 0100000000000003 -+ | 0200000000000001 | (000E) Path | C:\test.zip | 0100000000000003 -+ | 0200000000000001 | (000F) Hash | 0x00000000FEEDBEEF | 0100000000000003 ++ | 0200000000000001 | (000F) Path | C:\test.zip | 0100000000000003 ++ | 0200000000000001 | (0010) Hash | 0x00000000FEEDBEEF | 0100000000000003 diff --git a/tests/NexusMods.MnemonicDB.Tests/DbTests.cs b/tests/NexusMods.MnemonicDB.Tests/DbTests.cs index e4e26ee..1c54fc7 100644 --- a/tests/NexusMods.MnemonicDB.Tests/DbTests.cs +++ b/tests/NexusMods.MnemonicDB.Tests/DbTests.cs @@ -818,6 +818,26 @@ public async Task CanWriteTupleAttributes() var avet = Connection.Db.Datoms(SliceDescriptor.Create(File.TuplePath, (EntityId.From(0), ""), (EntityId.MaxValueNoPartition, ""), Connection.Db.Registry)); await VerifyTable(avet.Resolved()); } + + [Fact] + public async Task CanUseTuple3Attributes() + { + using var tx = Connection.BeginTransaction(); + var fileA = new File.New(tx) + { + Path = "C:\\test.txt", + Hash = Hash.From(0xDEADBEEF), + Size = Size.From(1), + ModId = EntityId.From(0), + TupleTest = (1, 2, 3) + }; + + var results = await tx.Commit(); + + var file = results.Remap(fileA); + + file.TupleTest.Should().Be((1, 2, 3)); + } [Fact] public async Task CanGetAnalyzerData()