using System; using System.IO; using System.Runtime.Serialization.Json; using System.Text; namespace Prophet.CoopWithDsh { internal sealed class SnapshotPublisher : IDisposable { private static readonly DataContractJsonSerializer Serializer = new DataContractJsonSerializer(typeof(OpenFileSnapshot)); private readonly object gate = new object(); internal string InstanceId { get; } = Guid.NewGuid().ToString("N"); internal string FilePath { get; } internal SnapshotPublisher(string? localAppData = null) { var root = localAppData ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); if (string.IsNullOrWhiteSpace(root)) throw new InvalidOperationException("LOCALAPPDATA is unavailable."); var directory = Path.Combine(root, "Prophet", "dsh-plugin-coop-with-vs", "v1", "instances"); FilePath = Path.Combine(directory, $"visualstudio-{InstanceId}.json"); } internal void Publish(OpenFileSnapshot snapshot) { lock (gate) { var directory = Path.GetDirectoryName(FilePath)!; Directory.CreateDirectory(directory); var temporary = FilePath + "." + Guid.NewGuid().ToString("N") + ".tmp"; using (var stream = new MemoryStream()) { Serializer.WriteObject(stream, snapshot); File.WriteAllText(temporary, Encoding.UTF8.GetString(stream.ToArray()) + Environment.NewLine, new UTF8Encoding(false)); } try { if (File.Exists(FilePath)) File.Replace(temporary, FilePath, null); else File.Move(temporary, FilePath); } catch { TryDelete(temporary); throw; } } } public void Dispose() => TryDelete(FilePath); private static void TryDelete(string path) { try { if (File.Exists(path)) File.Delete(path); } catch { } } } }