module account.data_impl_sqlite; import std.datetime; import d2sqlite3; import handy_httpd.components.optional; import account.data; import account.model; import money.currency; import history.model; import util.sqlite; class SqliteAccountRepository : AccountRepository { private Database db; this(Database db) { this.db = db; } Optional!Account findById(ulong id) { return findOne( db, "SELECT * FROM account WHERE id = ?", &parseAccount, id ); } Account insert(AccountType type, string numberSuffix, string name, Currency currency, string description) { Statement stmt = db.prepare(q"SQL INSERT INTO account (created_at, type, number_suffix, name, currency, description) VALUES (?, ?, ?, ?, ?, ?) SQL"); stmt.bind(1, Clock.currTime(UTC()).toISOExtString()); stmt.bind(2, type.id); stmt.bind(3, numberSuffix); stmt.bind(4, name); stmt.bind(5, currency.code); stmt.bind(6, description); stmt.execute(); ulong accountId = db.lastInsertRowid(); return findById(accountId).orElseThrow("Couldn't find account!"); } void setArchived(ulong id, bool archived) { util.sqlite.update( db, "UPDATE account SET archived = ? WHERE id = ?", archived ? 1 : 0, id ); } Account update(ulong id, in Account newData) { return newData; // TODO: } void deleteById(ulong id) { doTransaction(db, () { util.sqlite.update( db, "DELETE FROM history WHERE id IN ( SELECT history_id FROM account_history WHERE account_id = ? )", id ); util.sqlite.update(db, "DELETE FROM account WHERE id = ?", id); }); } Account[] findAll() { return util.sqlite.findAll(db, "SELECT * FROM account", &parseAccount); } AccountCreditCardProperties getCreditCardProperties(ulong id) { Statement stmt = db.prepare("SELECT * FROM account_credit_card_properties WHERE account_id = ?"); stmt.bind(1, id); ResultRange result = stmt.execute(); return parseCreditCardProperties(result.front); } void setCreditCardProperties(ulong id, in AccountCreditCardProperties props) { // TODO: } History getHistory(ulong id) { if (!exists(db, "SELECT id FROM account WHERE id = ?", id)) { throw new Exception("Account doesn't exist."); } Optional!History history = findOne( db, q"SQL SELECT * FROM history LEFT JOIN account_history ah ON ah.history_id = history.id WHERE ah.account_id = ? SQL", r => History(r.peek!ulong(0)), id ); if (!history.empty) { return history.value; } // No history exists yet, so add it. ulong historyId = doTransaction(db, () { util.sqlite.update(db, "INSERT INTO history DEFAULT VALUES"); ulong historyId = db.lastInsertRowid(); util.sqlite.update(db, "INSERT INTO account_history (account_id, history_id) VALUES (?, ?)", id, historyId); return historyId; }); return History(historyId); } static Account parseAccount(Row row) { return Account( row.peek!ulong(0), SysTime.fromISOExtString(row.peek!string(1)), row.peek!bool(2), AccountType.fromId(row.peek!string(3)), row.peek!string(4), row.peek!string(5), Currency.ofCode(row.peek!string(6)), row.peek!string(7) ); } static AccountCreditCardProperties parseCreditCardProperties(Row row) { import std.typecons : Nullable; ulong accountId = row.peek!ulong(0); Nullable!ulong creditLimit = row.peek!ulong(1); return AccountCreditCardProperties( accountId, creditLimit.isNull ? -1 : creditLimit.get() ); } }