2025-09-22 15:35:31 +02:00
|
|
|
using System.Text.Json;
|
|
|
|
|
using Models;
|
|
|
|
|
|
|
|
|
|
namespace Repository;
|
|
|
|
|
|
2025-10-28 12:05:35 +01:00
|
|
|
public class BookmarksRepository : IBookmarksRepository, IDisposable
|
2025-09-22 15:35:31 +02:00
|
|
|
{
|
|
|
|
|
private const string _appName = "Keychain";
|
2025-09-30 19:05:34 +02:00
|
|
|
private const string fileName = "password_stores.json";
|
2025-09-22 15:35:31 +02:00
|
|
|
|
|
|
|
|
private uint _autoIncrementedId;
|
|
|
|
|
private readonly string _filePath;
|
2025-10-29 13:23:56 +01:00
|
|
|
private List<Bookmark> _cache;
|
2025-09-22 15:35:31 +02:00
|
|
|
private bool _cacheAhead;
|
|
|
|
|
|
2025-10-28 12:05:35 +01:00
|
|
|
public BookmarksRepository()
|
2025-09-22 15:35:31 +02:00
|
|
|
{
|
|
|
|
|
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;
|
2025-09-30 19:05:34 +02:00
|
|
|
|
|
|
|
|
// Ensure Dispose is called when the process exits
|
|
|
|
|
AppDomain.CurrentDomain.ProcessExit += (s, e) => Dispose();
|
2025-09-22 15:35:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ReadAllFromFile()
|
|
|
|
|
{
|
2025-10-29 13:23:56 +01:00
|
|
|
var items = new List<Bookmark>();
|
2025-09-22 15:35:31 +02:00
|
|
|
|
|
|
|
|
if (File.Exists(_filePath))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string json = File.ReadAllText(_filePath);
|
2025-10-29 13:23:56 +01:00
|
|
|
items = JsonSerializer.Deserialize<List<Bookmark>>(json) ?? new List<Bookmark>();
|
2025-09-22 15:35:31 +02:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 13:23:56 +01:00
|
|
|
public List<Bookmark> GetAll()
|
2025-09-22 15:35:31 +02:00
|
|
|
{
|
|
|
|
|
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}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-29 13:23:56 +01:00
|
|
|
public void Create(Bookmark item)
|
2025-09-22 15:35:31 +02:00
|
|
|
{
|
|
|
|
|
item.ID = ++_autoIncrementedId;
|
|
|
|
|
_cache.Add(item);
|
|
|
|
|
_cacheAhead = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Delete(uint id)
|
|
|
|
|
{
|
2025-09-30 10:41:12 +02:00
|
|
|
_cache.RemoveAll(i => i.ID == id);
|
|
|
|
|
_cacheAhead = true;
|
2025-09-22 15:35:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteToStdErr(string message)
|
|
|
|
|
{
|
|
|
|
|
using var sw = new StreamWriter(Console.OpenStandardError());
|
|
|
|
|
sw.WriteLine(message);
|
|
|
|
|
}
|
|
|
|
|
}
|