Skip to content

Commit

Permalink
Merge pull request #56 from Nexus-Mods/dbattributes-fallback
Browse files Browse the repository at this point in the history
Add fallback for cases when an attribute doesn't exist in DI
  • Loading branch information
halgari authored May 7, 2024
2 parents 1dccdde + 0eef749 commit 972ecf4
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/NexusMods.MnemonicDB.Abstractions/Attribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ protected virtual TValueType FromLowLevel(ushort value, ValueTags tags)
throw new NotSupportedException("Unsupported low-level type " + value + " on attribute " + Id);
}

/// <summary>
/// Converts a low-level value to a high-level value
/// </summary>
protected virtual TValueType FromLowLevel(uint value, ValueTags tags)
{
throw new NotSupportedException("Unsupported low-level type " + value + " on attribute " + Id);
}


/// <summary>
/// Converts a low-level value to a high-level value
Expand Down Expand Up @@ -331,6 +339,7 @@ public TValueType ReadValue(ReadOnlySpan<byte> span)
ValueTags.Null => NullFromLowLevel(),
ValueTags.UInt8 => FromLowLevel(ReadUnmanaged<byte>(rest), tag),
ValueTags.UInt16 => FromLowLevel(ReadUnmanaged<ushort>(rest), tag),
ValueTags.UInt32 => FromLowLevel(ReadUnmanaged<uint>(rest), tag),
ValueTags.UInt64 => FromLowLevel(ReadUnmanaged<ulong>(rest), tag),
ValueTags.UInt128 => FromLowLevel(ReadUnmanaged<UInt128>(rest), tag),
ValueTags.Int16 => FromLowLevel(ReadUnmanaged<short>(rest), tag),
Expand Down
166 changes: 166 additions & 0 deletions src/NexusMods.MnemonicDB.Abstractions/Attributes/UnknownAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System;
using NexusMods.MnemonicDB.Abstractions.ElementComparers;

namespace NexusMods.MnemonicDB.Abstractions.Attributes;

/// <summary>
/// An unknown attribute is an attribute that exists in the database but is not known to the application. This is
/// either because the attribute was removed, or didn't show up in the DI system.
/// </summary>
/// <param name="dbAttribute"></param>
/// <typeparam name="TLowLevel"></typeparam>
public class UnknownAttribute<TLowLevel>(DbAttribute dbAttribute) :
Attribute<TLowLevel, TLowLevel>(dbAttribute.LowLevelType, dbAttribute.UniqueId.Namespace, dbAttribute.UniqueId.Name)
{
/// <inheritdoc />
protected override TLowLevel ToLowLevel(TLowLevel value)
{
return value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(byte value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(ushort value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(uint value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}


/// <inheritdoc />
protected override TLowLevel FromLowLevel(ulong value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(short value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(int value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(long value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(float value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(double value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}

/// <inheritdoc />
protected override TLowLevel FromLowLevel(string value, ValueTags tags)
{
if (tags != LowLevelType)
{
throw new ArgumentException($"Cannot convert {tags} to {LowLevelType}");
}

return (TLowLevel)(object)value;
}
}

/// <summary>
/// Helper class to create unknown attributes
/// </summary>
public static class UnknownAttribute
{
/// <summary>
/// Creates an unknown attribute from a database attribute
/// </summary>
public static IAttribute Create(DbAttribute dbAttribute)
{
return dbAttribute.LowLevelType switch
{
ValueTags.Null => new UnknownAttribute<Null>(dbAttribute),
ValueTags.UInt8 => new UnknownAttribute<byte>(dbAttribute),
ValueTags.UInt16 => new UnknownAttribute<ushort>(dbAttribute),
ValueTags.UInt32 => new UnknownAttribute<uint>(dbAttribute),
ValueTags.UInt64 => new UnknownAttribute<ulong>(dbAttribute),
ValueTags.UInt128 => new UnknownAttribute<ulong>(dbAttribute),
ValueTags.Int16 => new UnknownAttribute<short>(dbAttribute),
ValueTags.Int32 => new UnknownAttribute<int>(dbAttribute),
ValueTags.Int64 => new UnknownAttribute<long>(dbAttribute),
ValueTags.Int128 => new UnknownAttribute<long>(dbAttribute),
ValueTags.Float32 => new UnknownAttribute<float>(dbAttribute),
ValueTags.Float64 => new UnknownAttribute<double>(dbAttribute),
ValueTags.Ascii => new UnknownAttribute<string>(dbAttribute),
ValueTags.Utf8 => new UnknownAttribute<string>(dbAttribute),
ValueTags.Utf8Insensitive => new UnknownAttribute<string>(dbAttribute),
ValueTags.Blob => new UnknownAttribute<byte[]>(dbAttribute),
ValueTags.HashedBlob => new UnknownAttribute<byte[]>(dbAttribute),
ValueTags.Reference => new UnknownAttribute<EntityId>(dbAttribute),
_ => throw new ArgumentOutOfRangeException(nameof(dbAttribute.LowLevelType), dbAttribute.LowLevelType, null)
};
}
}
11 changes: 10 additions & 1 deletion src/NexusMods.MnemonicDB.Storage/AttributeRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Runtime.InteropServices;
using System.Threading;
using NexusMods.MnemonicDB.Abstractions;
using NexusMods.MnemonicDB.Abstractions.Attributes;
using NexusMods.MnemonicDB.Abstractions.Internals;
using Reloaded.Memory.Extensions;

Expand Down Expand Up @@ -69,6 +70,7 @@ public AttributeRegistry(IEnumerable<IAttribute> attributes)
/// <inheritdoc />
public RegistryId Id { get; }

/// <inheritdoc />
public IReadDatom Resolve(ReadOnlySpan<byte> datom)
{
var c = MemoryMarshal.Read<KeyPrefix>(datom);
Expand All @@ -77,11 +79,18 @@ public IReadDatom Resolve(ReadOnlySpan<byte> datom)
return attr.Resolve(c.E, c.A, datom.SliceFast(KeyPrefix.Size), c.T, c.IsRetract);
}

/// <inheritdoc />
public void Populate(DbAttribute[] attributes)
{
foreach (var dbAttribute in attributes)
{
var instance = _attributesBySymbol[dbAttribute.UniqueId];
// Do a try/get here because an attribute may not exist in code that exists in the database
if (!_attributesBySymbol.TryGetValue(dbAttribute.UniqueId, out var instance))
{
instance = UnknownAttribute.Create(dbAttribute);
_attributesBySymbol[dbAttribute.UniqueId] = instance;
}

instance.SetDbId(Id, dbAttribute.AttrEntityId);
_attributes[dbAttribute.AttrEntityId.Value] = instance;
}
Expand Down

0 comments on commit 972ecf4

Please sign in to comment.