import { Client } from "postgres"; import config from "./config.ts"; import { JSONObject } from "./JSON.ts"; import { Result, StoccaTreError } from "./Result.ts"; type Interpolable = number | string | bigint | null | boolean; type UnionToIntersection = (Union extends any ? (x: Union) => any : never) extends (x: infer Intersection) => any ? Intersection : never; type SQLWithArg = Arg extends string ? `${string}$${Arg}${string}` : never; type SQLWithArgs> = UnionToIntersection>; type ArgNamesInSQL = SQL extends `${string}$${infer Arg1} ${infer SQLAfterArg1}` ? Arg1 extends `${infer Arg1NoTrailing}${";" | "," | "'" | "\"" | "(" | ")"}` ? Arg1NoTrailing | ArgNamesInSQL : Arg1 | ArgNamesInSQL : SQL extends `${string}$${infer ArgN}` ? ArgN extends `${infer ArgNNoTrailing}${";" | "," | "'" | "\"" | "(" | ")"}` ? ArgNNoTrailing : ArgN : never; type ParametrisedSQL = `${string}$${string}`; type IsPlainSQL = T extends ParametrisedSQL ? never : T; type ArgsForSQL = SQL extends ParametrisedSQL ? Record, Interpolable> : JSONObject | undefined; // Helper for casting queries export type Q = Result; export type WithoutId = Omit; export interface StoccaTreDbConn { query(query: Q, ...args: Q extends IsPlainSQL ? [(JSONObject | undefined)?] : [ArgsForSQL]): Promise>; } export default async function createNewDbConnection(): Promise> { let postgresClient: Client; try { postgresClient = new Client({ user: config.dbUsername, database: "stocca_tre", hostname: config.hostname, port: config.dbPort, password: config.dbPassword, tls: { enabled: false, }, }); await postgresClient.connect(); } catch (e: unknown) { const error = e as { message?: string }; if (error.message) { return [new StoccaTreError(error.message).qualified("Error connecting to database: ")]; } return [new StoccaTreError("Error connecting to database.")]; } return [, { async query(query: Q, ...args: Q extends IsPlainSQL ? [(JSONObject | undefined)?] : [ArgsForSQL]): Promise> { try { const result = <{rows: JSONObject[]}> await postgresClient.queryObject(query, args[0]); return [, result.rows]; } catch (e: unknown) { const error = e as { message?: string }; return [new StoccaTreError(error.message ?? "Internal database error.")]; } }, }, ]; }