import fs from "fs/promises"; import path from "path"; import * as f from "fp-ts"; import * as t from "io-ts"; import * as tr from "io-ts/PathReporter"; export const PostDatabaseEntryC = t.type({ provider: t.literal("e926"), id: t.number, }); export type PostDatabaseEntry = t.TypeOf; export class PostDatabase { private entries: PostDatabaseEntry[] = []; private readonly filePath: string; private isLoaded = false; constructor(filename: string, private max?: number) { this.filePath = path.join(process.cwd(), filename); } private async load() { if (this.isLoaded) { return; } this.isLoaded = true; try { await fs.stat(this.filePath); } catch { await this.save(); } const fileContent = await fs.readFile(this.filePath, "utf8"); const entries = f.function.pipe( fileContent, f.json.parse, f.either.mapLeft(() => []), f.either.chain(t.array(PostDatabaseEntryC).decode) ); if (f.either.isRight(entries)) { this.entries = this.max ? entries.right.slice(-1 * this.max) : entries.right; } else { throw new Error(tr.failure(entries.left).join("\n\n")); } } private async save() { await fs.writeFile(this.filePath, JSON.stringify(this.entries), "utf8"); } async insertIfNotExists(entry: PostDatabaseEntry): Promise { await this.load(); const has = !!this.entries.find((e) => e.provider === entry.provider && e.id === entry.id); if (!has) { this.entries.push(entry); await this.save(); } return has; } async takeFirst(): Promise { await this.load(); const entry = this.entries.shift(); await this.save(); return entry; } async remove(entry: PostDatabaseEntry): Promise { await this.load(); const i = this.entries.findIndex((e) => e.provider === entry.provider && e.id === entry.id); if (i === -1) { return false; } this.entries.splice(i, 1); await this.save(); return true; } } export default PostDatabase;