#nullable enable using System; using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; namespace ShrinkSDK.Runtime { public enum ShrinkLogLevel { Trace, Debug, Information, Warning, Error } public interface IShrinkLogger { void Log(ShrinkLogLevel level, string message, Exception? exception = null); } public interface IShrinkMainThreadDispatcher { bool IsMainThread { get; } bool TryPost(Action action); } public interface IShrinkClock { DateTimeOffset UtcNow { get; } double UnscaledTimeSeconds { get; } } public interface IShrinkPathProvider { string PersistentDataPath { get; } string TemporaryDataPath { get; } } public interface IShrinkScreenshotProvider { ValueTask CapturePngAsync(CancellationToken cancellationToken = default); } public interface IShrinkApplicationLifecycle { event Action? Paused; event Action? Resumed; event Action? Exiting; } public static class ShrinkLoggerExtensions { public static void Error(this IShrinkLogger logger, string message, Exception? exception = null) => logger.Log(ShrinkLogLevel.Error, message, exception); } public static class ShrinkRuntimeServices { private static IShrinkLogger _logger = new ConsoleLogger(); private static IShrinkMainThreadDispatcher _dispatcher = new InlineDispatcher(); private static IShrinkClock _clock = new SystemClock(); private static IShrinkPathProvider _paths = new SystemPaths(); public static IShrinkLogger Logger => _logger; public static IShrinkMainThreadDispatcher Dispatcher => _dispatcher; public static IShrinkClock Clock => _clock; public static IShrinkPathProvider Paths => _paths; public static IShrinkScreenshotProvider? Screenshots { get; private set; } public static IShrinkApplicationLifecycle? Lifecycle { get; private set; } public static void Configure(IShrinkLogger logger, IShrinkMainThreadDispatcher dispatcher, IShrinkClock clock, IShrinkPathProvider paths, IShrinkScreenshotProvider? screenshots = null, IShrinkApplicationLifecycle? lifecycle = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); _clock = clock ?? throw new ArgumentNullException(nameof(clock)); _paths = paths ?? throw new ArgumentNullException(nameof(paths)); Screenshots = screenshots; Lifecycle = lifecycle; } private sealed class ConsoleLogger : IShrinkLogger { public void Log(ShrinkLogLevel level, string message, Exception? exception = null) { Console.WriteLine($"[{level}] {message}"); if (exception != null) Console.WriteLine(exception); } } private sealed class InlineDispatcher : IShrinkMainThreadDispatcher { public bool IsMainThread => true; public bool TryPost(Action action) { if (action == null) return false; action(); return true; } } private sealed class SystemClock : IShrinkClock { private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); public DateTimeOffset UtcNow => DateTimeOffset.UtcNow; public double UnscaledTimeSeconds => _stopwatch.Elapsed.TotalSeconds; } private sealed class SystemPaths : IShrinkPathProvider { public string PersistentDataPath => Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ShrinkSDK"); public string TemporaryDataPath => Path.GetTempPath(); } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] public sealed class ShrinkCodeGenWovenAttribute : Attribute { public ShrinkCodeGenWovenAttribute(string weaverVersion, string inputMvid) { WeaverVersion = weaverVersion; InputMvid = inputMvid; } public string WeaverVersion { get; } public string InputMvid { get; } } }