This commit is contained in:
Chris Kruining 2026-02-05 09:41:07 +01:00
parent 86aa0f856c
commit 0801ceee6a
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
310 changed files with 6712 additions and 418 deletions

View file

@ -0,0 +1,234 @@
using Microsoft.Data.Sqlite;
using Scry.Core.Models;
namespace Scry.Core.Data;
public class CardHashDatabase : IDisposable
{
private readonly SqliteConnection _connection;
private readonly string _dbPath;
public CardHashDatabase(string dbPath)
{
_dbPath = dbPath;
_connection = new SqliteConnection($"Data Source={dbPath}");
_connection.Open();
Initialize();
}
private void Initialize()
{
using var cmd = _connection.CreateCommand();
cmd.CommandText = """
CREATE TABLE IF NOT EXISTS card_hashes (
card_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
set_code TEXT NOT NULL,
collector_number TEXT,
hash BLOB NOT NULL,
image_uri TEXT
);
CREATE INDEX IF NOT EXISTS idx_card_hashes_name ON card_hashes(name);
CREATE INDEX IF NOT EXISTS idx_card_hashes_set ON card_hashes(set_code);
CREATE TABLE IF NOT EXISTS metadata (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
""";
cmd.ExecuteNonQuery();
}
public async Task<string?> GetMetadataAsync(string key, CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT value FROM metadata WHERE key = $key";
cmd.Parameters.AddWithValue("$key", key);
var result = await cmd.ExecuteScalarAsync(ct);
return result as string;
}
public async Task SetMetadataAsync(string key, string value, CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = """
INSERT OR REPLACE INTO metadata (key, value) VALUES ($key, $value)
""";
cmd.Parameters.AddWithValue("$key", key);
cmd.Parameters.AddWithValue("$value", value);
await cmd.ExecuteNonQueryAsync(ct);
}
public async Task InsertHashAsync(CardHash hash, CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = """
INSERT OR REPLACE INTO card_hashes
(card_id, name, set_code, collector_number, hash, image_uri)
VALUES ($card_id, $name, $set_code, $collector_number, $hash, $image_uri)
""";
cmd.Parameters.AddWithValue("$card_id", hash.CardId);
cmd.Parameters.AddWithValue("$name", hash.Name);
cmd.Parameters.AddWithValue("$set_code", hash.SetCode);
cmd.Parameters.AddWithValue("$collector_number", hash.CollectorNumber ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("$hash", hash.Hash);
cmd.Parameters.AddWithValue("$image_uri", hash.ImageUri ?? (object)DBNull.Value);
await cmd.ExecuteNonQueryAsync(ct);
}
public async Task InsertHashBatchAsync(IEnumerable<CardHash> hashes, CancellationToken ct = default)
{
await using var transaction = await _connection.BeginTransactionAsync(ct);
try
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = """
INSERT OR REPLACE INTO card_hashes
(card_id, name, set_code, collector_number, hash, image_uri)
VALUES ($card_id, $name, $set_code, $collector_number, $hash, $image_uri)
""";
var cardIdParam = cmd.Parameters.Add("$card_id", SqliteType.Text);
var nameParam = cmd.Parameters.Add("$name", SqliteType.Text);
var setCodeParam = cmd.Parameters.Add("$set_code", SqliteType.Text);
var collectorNumberParam = cmd.Parameters.Add("$collector_number", SqliteType.Text);
var hashParam = cmd.Parameters.Add("$hash", SqliteType.Blob);
var imageUriParam = cmd.Parameters.Add("$image_uri", SqliteType.Text);
foreach (var hash in hashes)
{
ct.ThrowIfCancellationRequested();
cardIdParam.Value = hash.CardId;
nameParam.Value = hash.Name;
setCodeParam.Value = hash.SetCode;
collectorNumberParam.Value = hash.CollectorNumber ?? (object)DBNull.Value;
hashParam.Value = hash.Hash;
imageUriParam.Value = hash.ImageUri ?? (object)DBNull.Value;
await cmd.ExecuteNonQueryAsync(ct);
}
await transaction.CommitAsync(ct);
}
catch
{
await transaction.RollbackAsync(ct);
throw;
}
}
public async Task<List<CardHash>> GetAllHashesAsync(CancellationToken ct = default)
{
var hashes = new List<CardHash>();
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT card_id, name, set_code, collector_number, hash, image_uri FROM card_hashes";
await using var reader = await cmd.ExecuteReaderAsync(ct);
while (await reader.ReadAsync(ct))
{
hashes.Add(new CardHash
{
CardId = reader.GetString(0),
Name = reader.GetString(1),
SetCode = reader.GetString(2),
CollectorNumber = reader.IsDBNull(3) ? null : reader.GetString(3),
Hash = (byte[])reader.GetValue(4),
ImageUri = reader.IsDBNull(5) ? null : reader.GetString(5)
});
}
return hashes;
}
public async Task<int> GetHashCountAsync(CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT COUNT(*) FROM card_hashes";
var result = await cmd.ExecuteScalarAsync(ct);
return Convert.ToInt32(result);
}
public async Task<CardHash?> GetHashByIdAsync(string cardId, CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = """
SELECT card_id, name, set_code, collector_number, hash, image_uri
FROM card_hashes WHERE card_id = $card_id
""";
cmd.Parameters.AddWithValue("$card_id", cardId);
await using var reader = await cmd.ExecuteReaderAsync(ct);
if (await reader.ReadAsync(ct))
{
return new CardHash
{
CardId = reader.GetString(0),
Name = reader.GetString(1),
SetCode = reader.GetString(2),
CollectorNumber = reader.IsDBNull(3) ? null : reader.GetString(3),
Hash = (byte[])reader.GetValue(4),
ImageUri = reader.IsDBNull(5) ? null : reader.GetString(5)
};
}
return null;
}
public async Task<HashSet<string>> GetExistingCardIdsAsync(CancellationToken ct = default)
{
var ids = new HashSet<string>();
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT card_id FROM card_hashes";
await using var reader = await cmd.ExecuteReaderAsync(ct);
while (await reader.ReadAsync(ct))
{
ids.Add(reader.GetString(0));
}
return ids;
}
public async Task<HashSet<string>> GetExistingCardNamesAsync(CancellationToken ct = default)
{
var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT DISTINCT name FROM card_hashes";
await using var reader = await cmd.ExecuteReaderAsync(ct);
while (await reader.ReadAsync(ct))
{
names.Add(reader.GetString(0));
}
return names;
}
public async Task DeleteByCardIdAsync(string cardId, CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "DELETE FROM card_hashes WHERE card_id = $card_id";
cmd.Parameters.AddWithValue("$card_id", cardId);
await cmd.ExecuteNonQueryAsync(ct);
}
public async Task ClearAsync(CancellationToken ct = default)
{
await using var cmd = _connection.CreateCommand();
cmd.CommandText = "DELETE FROM card_hashes";
await cmd.ExecuteNonQueryAsync(ct);
}
public void Dispose()
{
_connection.Dispose();
}
}