Registration: a concrete element type of an object list that is not keyed, and [MessagePackFormatter] on any type or member on the id path or on the id member, are refused. The id path is a fixed point over the walk's edges. The codec refuses, once per document type and on every encode and decode path, a resolver ahead of StandardResolver that formats a type on that path. UnionArmGuardTests moves StubArm out of the list union. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
65 lines
3 KiB
C#
65 lines
3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace GameCult.Caching.MessagePack
|
|
{
|
|
public sealed class CultCacheOpenOptions
|
|
{
|
|
public CultDocumentRegistry? Registry { get; set; }
|
|
|
|
public bool ReadOnly { get; set; }
|
|
|
|
public bool FlushOnDispose { get; set; }
|
|
|
|
public bool StoreFlushOnDispose { get; set; }
|
|
|
|
public bool UseDirectoryStore { get; set; }
|
|
}
|
|
|
|
public static class CultCacheMessagePack
|
|
{
|
|
// The store's encoding as one port. Values serialize without the document guard but with their owning document's
|
|
// options, resolvers included, so dictionary keys compare by the bytes the store writes; a document type
|
|
// deserializes as a registered document, a member type as itself.
|
|
public static CultCodec CreateCodec(CultDocumentRegistry registry) => new CultCodec(
|
|
(value, type, document) => global::MessagePack.MessagePackSerializer.Serialize(type, value, CultDocumentMessagePackSerialization.OptionsForDocument(document)),
|
|
(type, document, bytes) => type == document
|
|
? CultDocumentMessagePackSerialization.DeserializeUntyped(type, bytes, registry)
|
|
: global::MessagePack.MessagePackSerializer.Deserialize(type, bytes, CultDocumentMessagePackSerialization.OptionsForDocument(document)),
|
|
CultDocumentMessagePackSerialization.OverlaySlots);
|
|
|
|
// The inspection model over that codec.
|
|
public static CultInspectorModel CreateInspectorModel(CultDocumentRegistry registry) =>
|
|
new CultInspectorModel(registry, CreateCodec(registry));
|
|
|
|
// Opening hydrates. A consumer that wants a fresh store removes the records it does not keep, upserts, and
|
|
// flushes; the atomic replace does the rest.
|
|
public static CultCache Create(string filePath, CultCacheOpenOptions? options = null)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(filePath))
|
|
{
|
|
throw new ArgumentException("File path must be non-empty.", nameof(filePath));
|
|
}
|
|
|
|
options ??= new CultCacheOpenOptions();
|
|
var registry = options.Registry ?? CultDocumentRegistry.Shared;
|
|
var cache = new CultCache(registry, CreateCodec(registry))
|
|
{
|
|
FlushAttachedStoresOnDispose = options.FlushOnDispose
|
|
};
|
|
|
|
CacheBackingStore store = options.UseDirectoryStore || Directory.Exists(DirectoryMessagePackBackingStore.DefaultRecordDirectoryPath(filePath))
|
|
? new DirectoryMessagePackBackingStore(filePath, readOnly: options.ReadOnly)
|
|
: new SingleFileMessagePackBackingStore(filePath, options.ReadOnly);
|
|
store.FlushOnDispose = options.StoreFlushOnDispose;
|
|
cache.AddBackingStore(store);
|
|
return cache;
|
|
}
|
|
|
|
public static Task<CultCache> OpenAsync(string filePath, CultCacheOpenOptions? options = null)
|
|
{
|
|
return Task.FromResult(Create(filePath, options));
|
|
}
|
|
}
|
|
}
|