using System.Text.Json; using Models; namespace Repository; public class JsonRepository : IRepository, IDisposable { private const string _appName = "Keychain"; private const string fileName = "password_stores.json"; private uint _autoIncrementedId; private readonly string _filePath; private List _cache; private bool _cacheAhead; public JsonRepository() { string? xdgDataHome = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); string dataHome; if (!string.IsNullOrEmpty(xdgDataHome)) { dataHome = Path.Combine(xdgDataHome, _appName); } else { dataHome = Path.Combine( Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"), _appName ); } _filePath = Path.Combine(dataHome, fileName); ReadAllFromFile(); var lastItem = _cache.OrderBy(item => item.ID).LastOrDefault(); _autoIncrementedId = lastItem != null ? lastItem.ID : 0; // Ensure Dispose is called when the process exits AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose(); } private void ReadAllFromFile() { var items = new List(); if (File.Exists(_filePath)) { try { string json = File.ReadAllText(_filePath); items = JsonSerializer.Deserialize>(json) ?? new List(); } catch (JsonException e) { WriteToStdErr($"JSON error: {e.Message}"); } catch (IOException e) { WriteToStdErr($"File I/O error: {e.Message}"); } catch (Exception e) { WriteToStdErr($"Unexpected error: {e.Message}"); } } _cache = items; _cacheAhead = false; } public List GetAll() { return _cache; } public void Dispose() { if (_cacheAhead) { try { string json = JsonSerializer.Serialize(_cache); string? directory = Path.GetDirectoryName(_filePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory!); } File.WriteAllText(_filePath, json); } catch (IOException e) { WriteToStdErr($"File I/O error: {e.Message}"); } catch (Exception e) { WriteToStdErr($"Unexpected error: {e.Message}"); } } } public void Create(PasswordStore item) { item.ID = ++_autoIncrementedId; _cache.Add(item); _cacheAhead = true; } public void Delete(uint id) { _cache.RemoveAll(i => i.ID == id); _cacheAhead = true; } private void WriteToStdErr(string message) { using var sw = new StreamWriter(Console.OpenStandardError()); sw.WriteLine(message); } }