83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
import { Database, RestBindParameters } from "@db/sqlite";
|
|
import { err, getMessageFromError, ok, Result } from "@shared/utils/result.ts";
|
|
import { QueryExecutionError } from "@lib/errors.ts";
|
|
import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts";
|
|
import log from "@shared/utils/logger.ts";
|
|
|
|
export class DatabaseClient {
|
|
constructor(private readonly db: Database) {}
|
|
|
|
private safeExecute<T>(fn: () => T): Result<T, QueryExecutionError> {
|
|
try {
|
|
return ok(fn());
|
|
} catch (e) {
|
|
const message = getMessageFromError(e);
|
|
log.error(`Failed to execute sql! Error: ${e}`);
|
|
return err(new QueryExecutionError(message));
|
|
}
|
|
}
|
|
|
|
exec(
|
|
sql: string,
|
|
...params: RestBindParameters
|
|
): Result<number, QueryExecutionError> {
|
|
return this.safeExecute(() => this.db.exec(sql, params));
|
|
}
|
|
|
|
first<T extends object>(
|
|
sql: string,
|
|
...params: RestBindParameters
|
|
): Result<Option<T>, QueryExecutionError> {
|
|
return this.safeExecute(() =>
|
|
fromNullableVal(this.db.prepare(sql).get<T>(params))
|
|
);
|
|
}
|
|
|
|
all<T extends object>(
|
|
sql: string,
|
|
...params: RestBindParameters
|
|
): Result<Option<T[]>, QueryExecutionError> {
|
|
return this.safeExecute(() => this.db.prepare(sql).all<T>(params)).map(
|
|
(results) => (results.length > 0 ? some(results) : none),
|
|
);
|
|
}
|
|
|
|
prepareFetch<
|
|
T extends object,
|
|
P extends RestBindParameters = RestBindParameters,
|
|
>(sql: string): PreparedStatement<T> {
|
|
const stmt = this.db.prepare(sql);
|
|
|
|
const get = (
|
|
...params: P
|
|
): Result<Option<NonNullable<T>>, QueryExecutionError> =>
|
|
this.safeExecute(() => fromNullableVal(stmt.get<T>(params)));
|
|
|
|
const all = (
|
|
...params: P
|
|
): Result<Option<NonNullable<T[]>>, QueryExecutionError> =>
|
|
this.safeExecute(() => stmt.all<T>(params)).map((result) =>
|
|
result.length > 0 ? some(result) : none
|
|
);
|
|
|
|
return { get, all };
|
|
}
|
|
|
|
prepareExec<P extends RestBindParameters = RestBindParameters>(
|
|
sql: string,
|
|
): (...params: P) => Result<number, QueryExecutionError> {
|
|
const stmt = this.db.prepare(sql);
|
|
|
|
return (...params: P): Result<number, QueryExecutionError> => {
|
|
return this.safeExecute(() => stmt.run(params));
|
|
};
|
|
}
|
|
}
|
|
|
|
interface PreparedStatement<T extends object> {
|
|
get(...params: RestBindParameters): Result<Option<T>, QueryExecutionError>;
|
|
all(
|
|
...params: RestBindParameters
|
|
): Result<Option<T[]>, QueryExecutionError>;
|
|
}
|