using System.Text.Json; namespace AcDream.Launcher.Core.Updates; internal static class AtomicJsonFile { internal static async Task WriteAsync( string path, T value, JsonSerializerOptions options, CancellationToken cancellationToken = default) { string fullPath = Path.GetFullPath(path); string directory = Path.GetDirectoryName(fullPath) ?? throw new InvalidOperationException("The JSON path has no parent directory."); Directory.CreateDirectory(directory); string temporaryPath = Path.Combine( directory, $".{Path.GetFileName(fullPath)}.{Guid.NewGuid():N}.tmp"); try { await using (var stream = new FileStream( temporaryPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 16 * 1024, FileOptions.Asynchronous | FileOptions.WriteThrough)) { await JsonSerializer.SerializeAsync( stream, value, options, cancellationToken) .ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); stream.Flush(flushToDisk: true); } cancellationToken.ThrowIfCancellationRequested(); File.Move(temporaryPath, fullPath, overwrite: true); } finally { VerifiedArtifactDownloader.TryDelete(temporaryPath); } } internal static async Task WriteBytesAsync( string path, ReadOnlyMemory bytes, CancellationToken cancellationToken = default) { string fullPath = Path.GetFullPath(path); string directory = Path.GetDirectoryName(fullPath) ?? throw new InvalidOperationException("The file path has no parent directory."); Directory.CreateDirectory(directory); string temporaryPath = Path.Combine( directory, $".{Path.GetFileName(fullPath)}.{Guid.NewGuid():N}.tmp"); try { await using (var stream = new FileStream( temporaryPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 16 * 1024, FileOptions.Asynchronous | FileOptions.WriteThrough)) { await stream.WriteAsync(bytes, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); stream.Flush(flushToDisk: true); } cancellationToken.ThrowIfCancellationRequested(); File.Move(temporaryPath, fullPath, overwrite: true); } finally { VerifiedArtifactDownloader.TryDelete(temporaryPath); } } }