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.OptionsFor(document.Assembly)), (type, document, bytes) => type == document ? CultDocumentMessagePackSerialization.DeserializeUntyped(type, bytes, registry) : global::MessagePack.MessagePackSerializer.Deserialize(type, bytes, CultDocumentMessagePackSerialization.OptionsFor(document.Assembly)), 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 OpenAsync(string filePath, CultCacheOpenOptions? options = null) { return Task.FromResult(Create(filePath, options)); } } }