diff --git a/README.md b/README.md index 2b64779..8eb949d 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,25 @@ To create a new table, use the `createTable` command: ```typescript createTable('firstTable', { // inferred table name and entry - id: 'int', // only allows for known data types ('int', 'char', 'blob') - job: 'char', - name: 'char', - sex: 'char', - hasReadTheReadme: 'bool' + id: { + autoincrement: true, + type: 'INTEGER', // only allows for known data types ('int', 'char', 'blob') + nullable: false, + primary: true, + unique: true, + }, + job: { + type: 'char', + }, + name: { + type: 'char', + }, + sex: { + type: 'char', + }, + hasReadTheReadme: { + type: 'bool', + }, }) ``` diff --git a/playground/src/sibyl.ts b/playground/src/sibyl.ts index 534655e..a61eb74 100644 --- a/playground/src/sibyl.ts +++ b/playground/src/sibyl.ts @@ -24,12 +24,26 @@ const db = new SQL.Database() const { createTable, Insert, All, Select, Create, Update } = await Sibyl(db) createTable('orders', { - currency: 'char', - product: 'char', - id: 'int', - price: 'char', - booleanTest: 'bool', - status: 'varchar', + currency: { + type: 'char', + }, + product: { + type: 'char', + }, + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + }, + price: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, + status: { + type: 'varchar', + }, }) const insertions: Order[] = [] diff --git a/src/index.ts b/src/index.ts index 840f1ce..9311d05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,15 @@ import type { Database } from 'sql.js' import { buildSelectQuery, buildUpdateQuery, convertBooleanValues, convertCreateTableStatement, convertToObjects, formatInsertStatement, objectToWhereClause } from './sibylLib' -import type { DeleteArgs, SelectArgs, UpdateArgs } from './types' +import type { DBBlob, DBBoolean, DBDate, DBEntry, DBNumber, DBString, DBValue, DeleteArgs, SelectArgs, UpdateArgs } from './types' export default async function Sibyl>(db: Database) { type MappedTable = { [Key in keyof T]: - T[Key] extends boolean ? 'bool' : - T[Key] extends number ? 'int' | 'real' : - T[Key] extends string ? 'varchar' | 'char' : - T[Key] extends Date ? 'text' | 'int' | 'real' : - T[Key] extends Blob ? 'blob' : + T[Key] extends boolean ? DBValue : + T[Key] extends number ? DBValue : + T[Key] extends string ? DBValue : + T[Key] extends Date ? DBValue : + T[Key] extends Blob ? DBValue : null } diff --git a/src/sibylLib.ts b/src/sibylLib.ts index 2adfffa..95c53b4 100644 --- a/src/sibylLib.ts +++ b/src/sibylLib.ts @@ -1,4 +1,4 @@ -import type { DataStructure, SelectArgs, SibylResponse, UpdateArgs } from './types' +import type { DBEntry, DataStructure, SelectArgs, SibylResponse, UpdateArgs } from './types' export function formatInsertStatement>(table: string, structs: T[]) { const sortedStructs = sortKeys(structs) @@ -121,8 +121,26 @@ export function buildUpdateQuery(t } export function convertCreateTableStatement>(obj: T): string { let result = '' - for (const [columnName, columnType] of Object.entries(sortKeys([obj])[0])) - result += `${columnName} ${columnType}, ` + for (const [columnName, columnType] of Object.entries>(sortKeys([obj])[0])) { + result += columnName + + if (columnType.type) + result += ` ${columnType.type}` + + if (columnType.primary) + result += ' PRIMARY KEY' + + if (columnType.autoincrement) + result += ' AUTOINCREMENT' + + if (columnType.nullable === false) + result += ' NOT NULL' + + if (columnType.unique) + result += ' UNIQUE' + + result += ', ' + } result = result.slice(0, -2) return result diff --git a/src/tests/convertCreateTableStatement.test.ts b/src/tests/convertCreateTableStatement.test.ts index 3c81a51..0d1491d 100644 --- a/src/tests/convertCreateTableStatement.test.ts +++ b/src/tests/convertCreateTableStatement.test.ts @@ -1,21 +1,29 @@ import { describe, expect, it } from 'vitest' import { convertCreateTableStatement } from '../sibylLib' +import type { DBNumber, DBString, DBValue } from '../types' interface TableRow { - id: 'int' - location: 'char' - name: 'char' + id: DBValue + name: DBValue } describe('convertCreateTableStatement tests', () => { it('converts a table object to a statement', async () => { const actual = convertCreateTableStatement({ - name: 'char', - id: 'int', - location: 'char', + id: { + autoincrement: true, + type: 'INTEGER', + nullable: false, + primary: true, + unique: true, + }, + name: { + type: 'char', + nullable: false, + }, }) - const expectation = 'id int, location char, name char' + const expectation = 'id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, name char NOT NULL' expect(actual).toStrictEqual(expectation) }) }) diff --git a/src/tests/create.test.ts b/src/tests/create.test.ts index dcba48a..b475bb6 100644 --- a/src/tests/create.test.ts +++ b/src/tests/create.test.ts @@ -24,10 +24,21 @@ describe('create tests', () => { const { createTable, Create } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', - booleanTest: 'bool', + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, }) const actual = Create('first', { name: 'Craig', diff --git a/src/tests/delete.test.ts b/src/tests/delete.test.ts index 7d483af..b88f31c 100644 --- a/src/tests/delete.test.ts +++ b/src/tests/delete.test.ts @@ -23,9 +23,19 @@ describe('delete tests', () => { const { createTable, Insert, All, Delete } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', + id: { + autoincrement: true, + type: 'INTEGER', + nullable: false, + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, }) Insert('first', [ { @@ -43,16 +53,16 @@ describe('delete tests', () => { let actual = All('first') let expectation = [ - { - name: 'Craig', - id: 2344, - location: 'Brighton', - }, { id: 1, name: 'Bob', location: 'Cornwall', }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + }, ] expect(actual).toStrictEqual(expectation) diff --git a/src/tests/select.test.ts b/src/tests/select.test.ts index b770d87..cf8f14c 100644 --- a/src/tests/select.test.ts +++ b/src/tests/select.test.ts @@ -24,10 +24,22 @@ describe('select tests', () => { const { createTable, Create, Select } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', - booleanTest: 'bool', + id: { + primary: true, + autoincrement: true, + nullable: false, + type: 'INTEGER', + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, }) Create('first', { name: 'Craig', @@ -60,10 +72,22 @@ describe('select tests', () => { const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', - booleanTest: 'bool', + id: { + autoincrement: true, + type: 'INTEGER', + nullable: false, + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, }) Insert('first', [ { @@ -87,18 +111,18 @@ describe('select tests', () => { }) const expectation = [ - { - id: 2344, - location: 'Brighton', - name: 'Craig', - booleanTest: 1, - }, { name: 'Bob', id: 1, location: 'Brighton', booleanTest: 0, }, + { + id: 2344, + location: 'Brighton', + name: 'Craig', + booleanTest: 1, + }, ] expect(actual).toStrictEqual(expectation) }) @@ -112,10 +136,21 @@ describe('select tests', () => { const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', - booleanTest: 'bool', + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, }) Insert('first', [ { @@ -152,18 +187,18 @@ describe('select tests', () => { }) const expectation = [ - { - name: 'Craig', - id: 2344, - location: 'Brighton', - booleanTest: 1, - }, { name: 'Chris', id: 2, location: 'Cornwall', booleanTest: 0, }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: 1, + }, ] expect(actual).toStrictEqual(expectation) }) @@ -177,10 +212,22 @@ describe('select tests', () => { const { createTable, Insert, Select } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', - booleanTest: 'bool', + id: { + autoincrement: true, + type: 'INTEGER', + nullable: false, + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, }) Insert('first', [ { @@ -215,18 +262,18 @@ describe('select tests', () => { }) const expectation = [ - { - name: 'Craig', - id: 2344, - location: 'Brighton', - booleanTest: 1, - }, { name: 'Chris', id: 2, location: 'Cornwall', booleanTest: 0, }, + { + name: 'Craig', + id: 2344, + location: 'Brighton', + booleanTest: 1, + }, ] expect(actual).toStrictEqual(expectation) }) diff --git a/src/tests/update.test.ts b/src/tests/update.test.ts index 1e71d07..6121b6e 100644 --- a/src/tests/update.test.ts +++ b/src/tests/update.test.ts @@ -24,10 +24,21 @@ describe('update tests', () => { const { createTable, Insert, Update } = await Sibyl(db) createTable('first', { - id: 'int', - location: 'char', - name: 'char', - booleanTest: 'bool', + id: { + autoincrement: true, + type: 'INTEGER', + primary: true, + unique: true, + }, + location: { + type: 'char', + }, + name: { + type: 'char', + }, + booleanTest: { + type: 'bool', + }, }) Insert('first', [ { diff --git a/src/types.ts b/src/types.ts index 30b0903..6636455 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,21 @@ +export interface DBEntry { + type: T + primary?: boolean + nullable?: boolean + unique?: boolean + autoincrement: boolean +} + +export type DBBoolean = 'bool' +export type DBNumber = 'int' | 'real' | 'INTEGER' +export type DBString = 'varchar' | 'char' +export type DBDate = 'text' | 'int' | 'real' +export type DBBlob = 'blob' + +export type DBValue = T extends DBNumber + ? DBEntry + : Omit, 'autoincrement'> + export type SibylResponse = { [Key in keyof T]: T[Key] extends boolean ? 0 | 1 :