using System.Text.Json; namespace Lumi.Companion.SongOverlay; internal sealed class SongOverlaySettingsStore(string path) { private readonly SemaphoreSlim _gate = new(1, 1); public SongOverlaySettings Current { get; private set; } = new(); public async Task LoadAsync(CancellationToken cancellationToken = default) { if (!File.Exists(path)) return; try { Current = JsonSerializer.Deserialize( await File.ReadAllBytesAsync(path, cancellationToken), SongOverlayJson.Options) ?? new(); } catch (JsonException) { Current = new(); } } public void Save(SongOverlaySettings settings) { _gate.Wait(); try { Directory.CreateDirectory(Path.GetDirectoryName(path)!); var temporary = $"{path}.{Environment.ProcessId}.tmp"; try { File.WriteAllBytes(temporary, JsonSerializer.SerializeToUtf8Bytes(settings, SongOverlayJson.Options)); File.Move(temporary, path, true); } finally { File.Delete(temporary); } Current = settings; } finally { _gate.Release(); } } public async Task SaveAsync(SongOverlaySettings settings, CancellationToken cancellationToken = default) { await _gate.WaitAsync(cancellationToken); try { Directory.CreateDirectory(Path.GetDirectoryName(path)!); var temporary = $"{path}.{Environment.ProcessId}.tmp"; try { await File.WriteAllBytesAsync(temporary, JsonSerializer.SerializeToUtf8Bytes(settings, SongOverlayJson.Options), cancellationToken); File.Move(temporary, path, true); } finally { File.Delete(temporary); } Current = settings; } finally { _gate.Release(); } } } internal static class SongOverlayJson { public static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web) { DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }; }