using MoShiro.Backend.Application; namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteNodeBindingStore : INodeBindingStore { private const string Columns = "node_id, sha256, seq, signed, bound_at, violation, violation_at"; private readonly SqliteDatabase _db; public SqliteNodeBindingStore(SqliteDatabase db) { _db = db; } public ValueTask GetAsync(string nodeId, CancellationToken ct) => _db.ReadAsync(c => c.QueryOne($"SELECT {Columns} FROM node_bindings WHERE node_id = @id", Read, ("@id", nodeId)), ct); public ValueTask> ListAsync(CancellationToken ct) => _db.ReadAsync>(c => c.Query($"SELECT {Columns} FROM node_bindings ORDER BY node_id", Read), ct); public async ValueTask UpsertAsync(NodeBinding binding, CancellationToken ct) => await _db.WriteAsync( c => c.Exec( $"INSERT INTO node_bindings ({Columns}) VALUES (@id, @sha, @seq, @signed, @at, @violation, @violation_at) " + "ON CONFLICT(node_id) DO UPDATE SET sha256 = excluded.sha256, seq = excluded.seq, signed = excluded.signed, bound_at = excluded.bound_at, violation = excluded.violation, violation_at = excluded.violation_at", ("@id", binding.NodeId), ("@sha", binding.Sha256), ("@seq", binding.Seq), ("@signed", binding.Signed ? 1 : 0), ("@at", binding.BoundAtMs), ("@violation", binding.Violation), ("@violation_at", binding.ViolationAtMs)), ct); public ValueTask DeleteAsync(string nodeId, CancellationToken ct) => _db.WriteAsync(c => c.Exec("DELETE FROM node_bindings WHERE node_id = @id", ("@id", nodeId)) > 0, ct); private static NodeBinding Read(Microsoft.Data.Sqlite.SqliteDataReader r) => new(r.GetString(0), r.GetString(1), r.GetInt64(2), r.GetInt64(3) != 0, r.GetInt64(4), r.NullableString(5), r.NullableLong(6)); }