84 lines
3.1 KiB
TypeScript
84 lines
3.1 KiB
TypeScript
import { z } from "zod";
|
|
import { Store } from "./store.ts";
|
|
import { QueriedFor, QueriedOfField, StoredFor, Table } from "./typing.ts";
|
|
|
|
export class Entry<T extends Table<T>, S extends keyof T> {
|
|
public readonly store;
|
|
public readonly schema;
|
|
public readonly id;
|
|
|
|
public constructor(store: Store<T>, schema: S, id: string) {
|
|
this.store = store;
|
|
this.schema = schema;
|
|
this.id = id;
|
|
}
|
|
|
|
public async get<F extends keyof T[S]>(field: F): Promise<QueriedOfField<T, S, F>> {
|
|
if (!await this.store.entry_exists(this.schema, this.id)) throw new Error("Accessing a deleted entry");
|
|
const str = (value: unknown) => z.string().parse(value);
|
|
const content = await this.store.read_field(this.schema, this.id, field);
|
|
const kind = this.store.nodes.table[this.schema][field];
|
|
if (kind === "string") return z.string().parse(content) as QueriedFor<T, T[S][F]>;
|
|
if (kind === "number") return z.number().parse(content) as QueriedFor<T, T[S][F]>;
|
|
if (kind === "boolean") return z.boolean().parse(content) as QueriedFor<T, T[S][F]>;
|
|
if (kind[0] === "maybe") {
|
|
if (content === null) return null as QueriedFor<T, T[S][F]>;
|
|
else return new Entry(this.store, kind[1], str(content)) as QueriedFor<T, T[S][F]>;
|
|
}
|
|
if (kind[0] === "one") return new Entry(this.store, kind[1], str(content)) as QueriedFor<T, T[S][F]>;
|
|
if (kind[0] === "many") return new Collection(this.store, kind[1], str(content)) as QueriedFor<T, T[S][F]>;
|
|
throw new Error("Unreachable");
|
|
}
|
|
|
|
public async set<F extends keyof T[S]>(field: F, value: QueriedFor<T, T[S][F]>) {
|
|
let raw: string | number | boolean | null;
|
|
if (value instanceof Collection) raw = value.id;
|
|
else if (value instanceof Entry) raw = value.id;
|
|
else raw = value;
|
|
await this.store.write_field(this.schema, this.id, field, raw as StoredFor<T, T[S][F]>);
|
|
}
|
|
|
|
public async delete() {
|
|
const schema = this.store.nodes.table[this.schema];
|
|
for (const field in schema) {
|
|
const kind = schema[field];
|
|
if (Array.isArray(kind) && kind[0] === "many") {
|
|
// deno-lint-ignore no-explicit-any
|
|
const collection = await this.get(field) as Collection<T, any>;
|
|
for await (const value of collection) this.store.remove_relation(collection.id, value.id);
|
|
await this.store.delete_relations(collection.id);
|
|
}
|
|
await this.store.delete_field(this.schema, this.id, field);
|
|
}
|
|
await this.store.delete_entry(this.schema, this.id);
|
|
}
|
|
}
|
|
|
|
export class Collection<T extends Table<T>, S extends keyof T> {
|
|
public readonly store;
|
|
public readonly schema;
|
|
public readonly id;
|
|
|
|
public constructor(store: Store<T>, schema: S, collection_id: string) {
|
|
this.store = store;
|
|
this.schema = schema;
|
|
this.id = collection_id;
|
|
}
|
|
|
|
public async *[Symbol.asyncIterator]() {
|
|
for await (const id_ of this.store.list_relations(this.id)) {
|
|
const id = z.string().parse(id_);
|
|
const entry = new Entry(this.store, this.schema, id);
|
|
if (await this.store.entry_exists(this.schema, id)) yield entry;
|
|
else await this.remove(entry);
|
|
}
|
|
}
|
|
|
|
public async add(entry: Entry<T, S>) {
|
|
await this.store.add_relation(this.id, entry.id);
|
|
}
|
|
|
|
public async remove(entry: Entry<T, S>) {
|
|
await this.store.remove_relation(this.id, entry.id);
|
|
}
|
|
}
|