FileDatabase engine port from snailbird-content TS/Node program

This commit is contained in:
2025-09-01 16:55:28 -04:00
parent f0d60190cc
commit 9124e82e24
15 changed files with 1395 additions and 0 deletions
@@ -0,0 +1,9 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Represents a key for entries in the file database system.
/// Combines a string key with a media vault type for type-safe operations.
/// </summary>
/// <param name="Key">The string identifier for the entry</param>
/// <param name="Type">The media vault type this entry belongs to</param>
public record EntryKey(string Key, MediaVaultType Type);
@@ -0,0 +1,27 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Base interface for all index types
/// </summary>
public interface IIndex
{
/// <summary>
/// Gets the key identifier for this index
/// </summary>
string GetKey();
/// <summary>
/// Gets all entry keys in this index
/// </summary>
IReadOnlyList<EntryKey> GetEntries();
/// <summary>
/// Gets the number of entries in this index
/// </summary>
int GetEntriesSize();
/// <summary>
/// Checks if the index contains the specified entry key
/// </summary>
bool HasEntry(EntryKey entryKey);
}
@@ -0,0 +1,112 @@
using DeepDrftContent.FileDatabase.Utils;
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Base class for index data used in serialization
/// </summary>
public abstract class IndexData
{
public string IndexKey { get; }
protected IndexData(string indexKey)
{
IndexKey = indexKey;
}
}
/// <summary>
/// Serializable data for directory indexes
/// </summary>
public class DirectoryIndexData : IndexData
{
public List<EntryKey> Entries { get; set; } = new();
public DirectoryIndexData(string indexKey) : base(indexKey) { }
public static DirectoryIndexData FromIndex(DirectoryIndex index)
{
var data = new DirectoryIndexData(index.GetKey())
{
Entries = index.GetEntries().ToList()
};
return data;
}
}
/// <summary>
/// Serializable data for vault indexes
/// </summary>
public class VaultIndexData : IndexData
{
public List<(EntryKey Key, MetaData Value)> Entries { get; set; } = new();
public VaultIndexData(string indexKey) : base(indexKey) { }
public static VaultIndexData FromIndex(VaultIndex index)
{
var data = new VaultIndexData(index.GetKey())
{
Entries = index.Entries.Select(kvp => (kvp.Key, kvp.Value)).ToList()
};
return data;
}
}
/// <summary>
/// Directory index implementation using StructuralSet for entries
/// </summary>
public class DirectoryIndex : IndexData, IIndex
{
public StructuralSet<EntryKey> Entries { get; }
public DirectoryIndex(DirectoryIndexData indexData) : base(indexData.IndexKey)
{
Entries = new StructuralSet<EntryKey>();
// Load entries from data
foreach (var entry in indexData.Entries)
{
Entries.Add(entry);
}
}
public string GetKey() => IndexKey;
public IReadOnlyList<EntryKey> GetEntries() => Entries.ToList().AsReadOnly();
public int GetEntriesSize() => Entries.Size;
public bool HasEntry(EntryKey entryKey) => Entries.Has(entryKey);
public void PutEntry(EntryKey entryKey) => Entries.Add(entryKey);
}
/// <summary>
/// Vault index implementation using StructuralMap for entries with metadata
/// </summary>
public class VaultIndex : IndexData, IIndex
{
public StructuralMap<EntryKey, MetaData> Entries { get; }
public VaultIndex(VaultIndexData indexData) : base(indexData.IndexKey)
{
Entries = new StructuralMap<EntryKey, MetaData>();
// Load entries from data
foreach (var (key, value) in indexData.Entries)
{
Entries.Set(key, value);
}
}
public string GetKey() => IndexKey;
public IReadOnlyList<EntryKey> GetEntries() => Entries.Keys.ToList().AsReadOnly();
public int GetEntriesSize() => Entries.Size;
public bool HasEntry(EntryKey entryKey) => Entries.Has(entryKey);
public MetaData? GetEntry(EntryKey entryKey) => Entries.Get(entryKey);
public void PutEntry(EntryKey entryKey, MetaData metaData) => Entries.Set(entryKey, metaData);
}
@@ -0,0 +1,147 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Type mappings for media vault types to their corresponding classes
/// </summary>
public static class MediaVaultTypeMap
{
public static Type GetBinaryType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MediaBinary),
MediaVaultType.Image => typeof(ImageBinary),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
public static Type GetDtoType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MediaBinaryDto),
MediaVaultType.Image => typeof(ImageBinaryDto),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
public static Type GetParamsType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MediaBinaryParams),
MediaVaultType.Image => typeof(ImageBinaryParams),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
public static Type GetMetaDataType(MediaVaultType vaultType) => vaultType switch
{
MediaVaultType.Media => typeof(MetaData),
MediaVaultType.Image => typeof(ImageMetaData),
_ => throw new ArgumentException($"Unknown vault type: {vaultType}")
};
}
/// <summary>
/// Factory for creating metadata objects based on vault type
/// </summary>
public static class MetaDataFactory
{
public static MetaData Create(MediaVaultType type, string entryKey, string extension, double aspectRatio = 1.0)
{
return type switch
{
MediaVaultType.Media => new MetaData(entryKey, extension),
MediaVaultType.Image => new ImageMetaData(entryKey, extension, aspectRatio),
_ => throw new ArgumentException($"Unknown vault type: {type}")
};
}
public static T Create<T>(MediaVaultType type, string entryKey, string extension, double aspectRatio = 1.0)
where T : MetaData
{
var metaData = Create(type, entryKey, extension, aspectRatio);
return (T)metaData;
}
}
/// <summary>
/// Factory for creating media parameter objects
/// </summary>
public static class MediaParamsFactory
{
public static object Create(MediaVaultType type, FileBinary fileBinary, MetaData metaData)
{
return type switch
{
MediaVaultType.Media => new MediaBinaryParams(fileBinary.Buffer, fileBinary.Size, metaData.Extension),
MediaVaultType.Image when metaData is ImageMetaData imageMetaData =>
new ImageBinaryParams(fileBinary.Buffer, fileBinary.Size, metaData.Extension, imageMetaData.AspectRatio),
_ => throw new ArgumentException($"Invalid vault type {type} or metadata type mismatch")
};
}
public static T Create<T>(MediaVaultType type, FileBinary fileBinary, MetaData metaData)
{
var parameters = Create(type, fileBinary, metaData);
return (T)parameters;
}
}
/// <summary>
/// Factory for creating media binary objects from parameters
/// </summary>
public static class FileBinaryFactory
{
public static object Create(MediaVaultType vaultType, object parameters)
{
return vaultType switch
{
MediaVaultType.Media when parameters is MediaBinaryParams mediaParams =>
new MediaBinary(mediaParams),
MediaVaultType.Image when parameters is ImageBinaryParams imageParams =>
new ImageBinary(imageParams),
_ => throw new ArgumentException($"Invalid vault type {vaultType} or parameter type mismatch")
};
}
public static T Create<T>(MediaVaultType vaultType, object parameters) where T : FileBinary
{
var binary = Create(vaultType, parameters);
return (T)binary;
}
public static object From(MediaVaultType type, object mediaBinaryDto)
{
return type switch
{
MediaVaultType.Media when mediaBinaryDto is MediaBinaryDto mediaDto =>
MediaBinary.From(mediaDto),
MediaVaultType.Image when mediaBinaryDto is ImageBinaryDto imageDto =>
ImageBinary.From(imageDto),
_ => throw new ArgumentException($"Invalid type {type} or DTO type mismatch")
};
}
public static T From<T>(MediaVaultType type, object mediaBinaryDto) where T : FileBinary
{
var binary = From(type, mediaBinaryDto);
return (T)binary;
}
}
/// <summary>
/// Factory for creating DTO objects from media binaries
/// </summary>
public static class FileBinaryDtoFactory
{
public static object From(MediaVaultType type, object mediaBinary)
{
return type switch
{
MediaVaultType.Media when mediaBinary is MediaBinary media =>
new MediaBinaryDto(media),
MediaVaultType.Image when mediaBinary is ImageBinary image =>
new ImageBinaryDto(image),
_ => throw new ArgumentException($"Invalid type {type} or binary type mismatch")
};
}
public static T From<T>(MediaVaultType type, object mediaBinary)
{
var dto = From(type, mediaBinary);
return (T)dto;
}
}
@@ -0,0 +1,175 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Parameters for creating a FileBinary
/// </summary>
/// <param name="Buffer">The binary data</param>
/// <param name="Size">The size of the data in bytes</param>
public record FileBinaryParams(byte[] Buffer, int Size);
/// <summary>
/// Base class for file binary data
/// </summary>
public class FileBinary
{
public byte[] Buffer { get; }
public int Size { get; }
public FileBinary(FileBinaryParams parameters)
{
Buffer = parameters.Buffer;
Size = parameters.Size;
}
public static FileBinary From(FileBinaryDto dto)
{
var buffer = Convert.FromBase64String(dto.Base64);
return new FileBinary(new FileBinaryParams(buffer, dto.Size));
}
}
/// <summary>
/// DTO for FileBinary serialization
/// </summary>
/// <param name="Base64">Base64 encoded binary data</param>
/// <param name="Size">Size of the original data</param>
public record FileBinaryDto(string Base64, int Size)
{
public FileBinaryDto(FileBinary fileBinary) : this(
Convert.ToBase64String(fileBinary.Buffer),
fileBinary.Size) { }
}
/// <summary>
/// Parameters for creating a MediaBinary
/// </summary>
/// <param name="Buffer">The binary data</param>
/// <param name="Size">The size of the data in bytes</param>
/// <param name="Extension">The file extension</param>
public record MediaBinaryParams(byte[] Buffer, int Size, string Extension)
: FileBinaryParams(Buffer, Size);
/// <summary>
/// Media binary with extension information
/// </summary>
public class MediaBinary : FileBinary
{
public string Extension { get; }
public MediaBinary(MediaBinaryParams parameters) : base(parameters)
{
Extension = parameters.Extension;
}
public static MediaBinary From(MediaBinaryDto dto)
{
var buffer = Convert.FromBase64String(dto.Base64);
var extension = GetExtensionType(dto.Mime);
return new MediaBinary(new MediaBinaryParams(buffer, dto.Size, extension));
}
private static string GetExtensionType(string mime)
{
return MimeTypeExtensions.GetExtension(mime);
}
}
/// <summary>
/// DTO for MediaBinary serialization
/// </summary>
/// <param name="Base64">Base64 encoded binary data</param>
/// <param name="Size">Size of the original data</param>
/// <param name="Mime">MIME type of the media</param>
public record MediaBinaryDto(string Base64, int Size, string Mime) : FileBinaryDto(Base64, Size)
{
public MediaBinaryDto(MediaBinary mediaBinary) : this(
Convert.ToBase64String(mediaBinary.Buffer),
mediaBinary.Size,
MimeTypeExtensions.GetMimeType(mediaBinary.Extension)) { }
}
/// <summary>
/// Parameters for creating an ImageBinary
/// </summary>
/// <param name="Buffer">The binary data</param>
/// <param name="Size">The size of the data in bytes</param>
/// <param name="Extension">The file extension</param>
/// <param name="AspectRatio">The aspect ratio of the image</param>
public record ImageBinaryParams(byte[] Buffer, int Size, string Extension, double AspectRatio)
: MediaBinaryParams(Buffer, Size, Extension);
/// <summary>
/// Image binary with aspect ratio information
/// </summary>
public class ImageBinary : MediaBinary
{
public double AspectRatio { get; }
public ImageBinary(ImageBinaryParams parameters) : base(parameters)
{
AspectRatio = parameters.AspectRatio;
}
public static ImageBinary From(ImageBinaryDto dto)
{
var buffer = Convert.FromBase64String(dto.Base64);
var extension = GetExtensionType(dto.Mime);
return new ImageBinary(new ImageBinaryParams(buffer, dto.Size, extension, dto.AspectRatio));
}
private static string GetExtensionType(string mime)
{
return MimeTypeExtensions.GetExtension(mime);
}
}
/// <summary>
/// DTO for ImageBinary serialization
/// </summary>
/// <param name="Base64">Base64 encoded binary data</param>
/// <param name="Size">Size of the original data</param>
/// <param name="Mime">MIME type of the media</param>
/// <param name="AspectRatio">The aspect ratio of the image</param>
public record ImageBinaryDto(string Base64, int Size, string Mime, double AspectRatio)
: MediaBinaryDto(Base64, Size, Mime)
{
public ImageBinaryDto(ImageBinary imageBinary) : this(
Convert.ToBase64String(imageBinary.Buffer),
imageBinary.Size,
MimeTypeExtensions.GetMimeType(imageBinary.Extension),
imageBinary.AspectRatio) { }
}
/// <summary>
/// Utility class for MIME type and extension conversions
/// </summary>
public static class MimeTypeExtensions
{
private static readonly Dictionary<string, string> MimeTypes = new()
{
{ ".jpg", "image/jpeg" },
{ ".jpeg", "image/jpeg" },
{ ".png", "image/png" },
{ ".gif", "image/gif" },
{ ".webp", "image/webp" },
{ ".svg", "image/svg+xml" },
{ ".bmp", "image/bmp" }
};
private static readonly Dictionary<string, string> Extensions =
MimeTypes.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
public static string GetMimeType(string extension)
{
return MimeTypes.TryGetValue(extension.ToLowerInvariant(), out var mime)
? mime
: "application/octet-stream";
}
public static string GetExtension(string mime)
{
return Extensions.TryGetValue(mime.ToLowerInvariant(), out var extension)
? extension
: ".bin";
}
}
@@ -0,0 +1,10 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Enum representing different types of media vaults
/// </summary>
public enum MediaVaultType
{
Media,
Image
}
@@ -0,0 +1,17 @@
namespace DeepDrftContent.FileDatabase.Models;
/// <summary>
/// Base metadata for media entries
/// </summary>
/// <param name="MediaKey">The key used to identify the media file</param>
/// <param name="Extension">The file extension of the media</param>
public record MetaData(string MediaKey, string Extension);
/// <summary>
/// Extended metadata for image entries, including aspect ratio
/// </summary>
/// <param name="MediaKey">The key used to identify the media file</param>
/// <param name="Extension">The file extension of the media</param>
/// <param name="AspectRatio">The aspect ratio of the image</param>
public record ImageMetaData(string MediaKey, string Extension, double AspectRatio)
: MetaData(MediaKey, Extension);