teacher-tools/api/source/db.d

79 lines
2.1 KiB
D
Raw Normal View History

2024-12-16 22:20:15 +00:00
module db;
import std.algorithm;
import std.array;
import std.typecons;
import std.conv;
import d2sqlite3;
import slf4d;
import handy_httpd.components.optional;
struct Column {
const string name;
}
Database getDb() {
import std.file;
bool shouldInitDb = !exists("teacher-tools.db");
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
if (d2sqlite3.threadSafe()) {
flags |= SQLITE_OPEN_NOMUTEX;
}
Database db = Database("teacher-tools.db", flags);
db.execute("PRAGMA foreign_keys=ON");
const string schema = import("schema.sql");
if (shouldInitDb) {
db.run(schema);
info("Initialized database schema.");
}
return db;
}
private string[] getColumnNames(T)() {
import std.string : toLower;
alias members = __traits(allMembers, T);
string[members.length] columnNames;
static foreach (i; 0 .. members.length) {
static if (__traits(getAttributes, __traits(getMember, T, members[i])).length > 0) {
columnNames[i] = toLower(__traits(getAttributes, __traits(getMember, T, members[i]))[0].name);
} else {
columnNames[i] = toLower(members[i]);
}
}
return columnNames.dup;
}
private string getArgsStr(T)() {
import std.traits : RepresentationTypeTuple;
alias types = RepresentationTypeTuple!T;
string argsStr = "";
static foreach (i, type; types) {
argsStr ~= "row.peek!(" ~ type.stringof ~ ")(" ~ i.to!string ~ ")";
static if (i + 1 < types.length) {
argsStr ~= ", ";
}
}
return argsStr;
}
T parseRow(T)(Row row) {
mixin("T t = T(" ~ getArgsStr!T ~ ");");
return t;
}
T[] findAll(T, Args...)(Database db, string query, Args args) {
Statement stmt = db.prepare(query);
stmt.bindAll(args);
ResultRange result = stmt.execute();
return result.map!(row => parseRow!T(row)).array;
}
Optional!T findOne(T, Args...)(Database db, string query, Args args) {
Statement stmt = db.prepare(query);
stmt.bindAll(args);
ResultRange result = stmt.execute();
if (result.empty) return Optional!T.empty;
return Optional!T.of(parseRow!T(result.front));
}