#nullable enable using System; using Cysharp.Threading.Tasks; using ShrinkApp; using ShrinkContext; namespace ShrinkDataSaver.Integration.App { [ShrinkAppModuleInstaller] [Obsolete("ClassicHost compatibility only. ContextLoader projects should use ShrinkDataSaverAppComponent from a composition root.")] public sealed class ShrinkDataSaverAppInstaller : IShrinkAppModuleInstaller { public string ModuleId => "shrink.datasaver"; public int Order => -2000; public System.Collections.Generic.IReadOnlyList DependsOn => Array.Empty(); public void RegisterServices(ShrinkAppContext context) { context.Services.Register(new ShrinkDataSaverService()); } public UniTask InitializeAsync(ShrinkAppContext context) { ShrinkDataSaverRuntime.Initialize(); return UniTask.CompletedTask; } } /// 不产生存档写入的读取能力。 public interface IShrinkDataSaverReader { int LoadedSlot { get; } int GetRecentSlotIndex(); UniTask GetRecommendedContinueSlotAsync(); UniTask GetAllMetaAsync(); } /// 完整存档能力;社区模组可通过 intercept 被降权为仅 public interface IShrinkDataSaverWriter : IShrinkDataSaverReader { UniTask SaveSlotAsync(int slotIndex, SaveOptions? options = null); UniTask LoadSlotAsync(int slotIndex, string? decryptionKey = null); } public static class ShrinkDataSaverAccess { public const string MetadataKey = "access"; public const string ReadOnly = "read-only"; } public sealed class ShrinkDataSaverService : IShrinkDataSaverWriter { public int LoadedSlot => ShrinkSave.LoadedSlot; public int GetRecentSlotIndex() => ShrinkSave.GetRecentSlotIndex(); public UniTask GetRecommendedContinueSlotAsync() => ShrinkSave.GetRecommendedContinueSlotAsync(); public UniTask GetAllMetaAsync() => ShrinkSave.GetAllMetaAsync(); public UniTask SaveSlotAsync(int slotIndex, SaveOptions? options = null) => ShrinkSave.SaveSlotAsync(slotIndex, options); public UniTask LoadSlotAsync(int slotIndex, string? decryptionKey = null) => ShrinkSave.LoadSlotAsync(slotIndex, decryptionKey); } internal sealed class ShrinkDataSaverReadOnlyService : IShrinkDataSaverReader { private readonly IShrinkDataSaverReader _inner; public ShrinkDataSaverReadOnlyService(IShrinkDataSaverReader inner) { _inner = inner ?? throw new ArgumentNullException(nameof(inner)); } public int LoadedSlot => _inner.LoadedSlot; public int GetRecentSlotIndex() => _inner.GetRecentSlotIndex(); public UniTask GetRecommendedContinueSlotAsync() => _inner.GetRecommendedContinueSlotAsync(); public UniTask GetAllMetaAsync() => _inner.GetAllMetaAsync(); } /// DataSaver provider 侧的访问介导;仅约束经 ShrinkCtx.Get 取得的能力。 internal sealed class ShrinkDataSaverAccessPolicy : IShrinkCoeffectAccessPolicy { private readonly ShrinkDataSaverReadOnlyService _readOnly; public ShrinkDataSaverAccessPolicy(ShrinkDataSaverService full) { _readOnly = new ShrinkDataSaverReadOnlyService( full ?? throw new ArgumentNullException(nameof(full))); } public object? Resolve(ShrinkCoeffectAccessContext context, object? value) { if (!context.TryGetMetadata(ShrinkDataSaverAccess.MetadataKey, out var access) || !string.Equals(access, ShrinkDataSaverAccess.ReadOnly, StringComparison.Ordinal)) return value; if (typeof(IShrinkDataSaverWriter).IsAssignableFrom(context.RequestedType) || context.RequestedType == typeof(ShrinkDataSaverService)) { throw new ShrinkCoeffectAccessDeniedException(context.Key, "the current context grants read-only DataSaver access"); } if (context.RequestedType == typeof(object) || context.RequestedType.IsInstanceOfType(_readOnly)) return _readOnly; throw new ShrinkCoeffectAccessDeniedException(context.Key, $"read-only DataSaver view cannot satisfy requested type '{context.RequestedType.FullName}'"); } } }