sql/util.ts: Add a simple SQL record mapper

sql-bricks can write queries for us, and the mapper handles type
conversions to and from SQL-friendly strings.
This commit is contained in:
Tau
2019-10-31 18:01:13 -04:00
parent d58f4bed8f
commit beb9144e9b

86
src/sql/util.ts Normal file
View File

@@ -0,0 +1,86 @@
import snakeCase from "snake-case";
import { Row } from "./api";
interface ColMapper<F> {
_read(str: string): F;
_write(val: F): string;
}
type Spec<R> = {
[K in keyof R]: ColMapper<R[K]>;
};
/**
* Function objects describing the precise way in which our SQL driver
* transmits and receives values to the database as strings. Note that we could
* potentially have more than one SQL driver so this probably needs to be
* pushed down into `/sql/api.ts` somehow...
*/
export const T = {
bigint: {
_read: (str: string) => BigInt(str),
_write: (val: bigint) => val.toString(),
},
boolean: {
_read: (str: string) => str === "true",
_write: (val: boolean) => val.toString(),
},
number: {
_read: (str: string) => parseInt(str),
_write: (val: number) => val.toString(),
},
string: {
_read: (str: string) => str,
_write: (val: string) => val,
},
Date: {
_read: (str: string) => new Date(str),
_write: (val: Date) => val.toISOString(),
},
};
/**
* Boilerplate-reducing SQL <-> JavaScript record mapper. The TypeScript
* compiler will ensure that the mapping is correct.
*/
export function createSqlMapper<R>(spec: Spec<R>) {
const snaked = new Map<string, string>();
const colNames = new Array<string>();
for (const k in spec) {
const sk = snakeCase(k);
snaked.set(k, sk);
colNames.push(sk);
}
function readRow(row: Row): R {
const result = {} as R;
for (const k in spec) {
const sk = snaked.get(k);
const v = spec[k]._read(row[sk!]);
result[k] = v;
}
return result;
}
function writeRow(obj: R): Row {
const result = {};
for (const k in spec) {
const sk = snaked.get(k);
const v = spec[k]._write(obj[k]);
result[sk!] = v;
}
return result;
}
return { readRow, writeRow, colNames };
}