import type { BuildEvents } from "#Source/event/index.ts" import { EventManager } from "#Source/event/index.ts" import { Mutex } from "#Source/orchestration/index.ts" /** * @description 表式运行时存储结构的事件集合。 */ export type TableEvents = BuildEvents<{ valueChanged: (rows: Row[]) => void }> /** * @description 表行数据的基础结构。 */ export type BaseRow = Record /** * @description 表式运行时存储结构的构造选项。 */ export interface BaseTableOptions { /** * @description 返回行的唯一标识。 * 不要在这个回调里修改行数据。 */ uniqueGetter: (row: Row) => string } /** * @description 以唯一标识组织行数据集合的基础表模型。 */ export abstract class BaseTable { protected options: BaseTableOptions protected map: Map protected mutex: Mutex /** * @description 表数据变更事件管理器。 */ event: EventManager> /** * @description 创建一个基础表实例。 */ constructor(options: BaseTableOptions) { this.options = options this.map = new Map() this.mutex = new Mutex() this.event = new EventManager() } protected getUnique(row: RowType): string { return this.options.uniqueGetter(row) } private internalEmitValueChanged(): void { this.event.emit("valueChanged", structuredClone(Array.from(this.map.values()))) } private internalAssertBatchRowsHaveUniqueValues(rows: RowType[]): void { const uniqueSet = new Set() for (const row of rows) { const unique = this.getUnique(row) if (uniqueSet.has(unique)) { throw new Error( `Duplicate row unique "${unique}" found in batch, failed row: ${JSON.stringify(row)}`, ) } uniqueSet.add(unique) } } private internalFindRows(getter: (row: RowType) => boolean): RowType[] { const matchedRows: RowType[] = [] for (const row of this.map.values()) { if (getter(structuredClone(row))) { matchedRows.push(row) } } return matchedRows } private internalSetRows(rows: RowType[]): void { for (const row of structuredClone(rows)) { const unique = this.getUnique(row) this.map.set(unique, row) } this.internalEmitValueChanged() } private internalUnsetRows(rows: RowType[]): void { for (const row of structuredClone(rows)) { const unique = this.getUnique(row) this.map.delete(unique) } this.internalEmitValueChanged() } /** * @description 清空整张表。 */ async clearTable(): Promise { return await this.mutex.runExclusive(() => { this.map.clear() this.internalEmitValueChanged() }) } /** * @description 按条件读取第一条匹配的行快照。 * * @returns { Promise } Row, or undefined if not found. */ async getRow(getter: (row: RowType) => boolean): Promise { return await this.mutex.runExclusive(() => { const row = this.internalFindRows(getter)[0] return structuredClone(row) }) } /** * @description 按条件读取全部匹配的行快照。 * * @returns { Promise } Rows. */ async getRows(getter: (row: RowType) => boolean): Promise { return await this.mutex.runExclusive(() => { const rows = this.internalFindRows(getter) return structuredClone(rows) }) } /** * @description 安全插入一条新行。 * * @returns { Promise } Inserted row. * * @throws { Error } If row with unique already exists. */ async insertRowOrThrow(row: RowType): Promise { return await this.mutex.runExclusive(() => { const unique = this.getUnique(row) if (this.map.has(unique)) { throw new Error( `Row with unique "${unique}" already exists, failed row: ${JSON.stringify(row)}`, ) } this.internalSetRows([row]) return structuredClone(row) }) } /** * @description 插入一条新行;若唯一标识已存在则返回 undefined。 * * @returns { Promise } Inserted row, or undefined if row with unique already * exists. */ async insertRow(row: RowType): Promise { try { return await this.insertRowOrThrow(row) } catch { return undefined } } /** * @description 安全插入多条新行。 * * @returns { Promise } Inserted rows. * * @throws { Error } If row with unique already exists. */ async insertRowsOrThrow(rows: RowType[]): Promise { return await this.mutex.runExclusive(() => { this.internalAssertBatchRowsHaveUniqueValues(rows) const toInsertRows: RowType[] = [] for (const row of rows) { const unique = this.getUnique(row) if (this.map.has(unique)) { throw new Error( `Row with unique "${unique}" already exists, failed row: ${JSON.stringify(row)}`, ) } toInsertRows.push(row) } this.internalSetRows(toInsertRows) return structuredClone(rows) }) } /** * @description 按唯一标识做浅合并更新,并返回更新后的行。 * * @throws { Error } If row not found. */ async simpleUpdateRowOrThrow(row: RowType): Promise { return await this.mutex.runExclusive(() => { const unique = this.getUnique(row) const existRow = this.map.get(unique) if (existRow === undefined) { throw new Error(`Row not found, target row: ${JSON.stringify(row)}`) } const updatedRow = { ...existRow, ...row } this.internalSetRows([updatedRow]) return structuredClone(updatedRow) }) } /** * @description 按唯一标识做浅合并更新;若行不存在则返回 undefined。 */ async simpleUpdateRow(row: RowType): Promise { try { return await this.simpleUpdateRowOrThrow(row) } catch { return undefined } } /** * @description 按唯一标识批量做浅合并更新,并返回更新后的行。 * * @throws { Error } If any row is not found or the batch contains duplicate uniques. */ async simpleUpdateRowsOrThrow(rows: RowType[]): Promise { return await this.mutex.runExclusive(() => { this.internalAssertBatchRowsHaveUniqueValues(rows) const updatedRows: RowType[] = [] for (const row of rows) { const unique = this.getUnique(row) const existRow = this.map.get(unique) if (existRow === undefined) { throw new Error(`Row not found, target row: ${JSON.stringify(row)}`) } updatedRows.push({ ...existRow, ...row }) } this.internalSetRows(updatedRows) return structuredClone(updatedRows) }) } /** * @description 按条件查找第一条行并用 updater 生成新值。 * * @returns { Promise } Updated row. * * @throws { Error } If row not found, or unique changed between before and after. */ async updateRowOrThrow( getter: (row: RowType) => boolean, updater: (row: RowType) => RowType, ): Promise { return await this.mutex.runExclusive(() => { const row = this.internalFindRows(getter)[0] if (row === undefined) { throw new Error("Row not found.") } const oldUnique = this.getUnique(row) const newRow = updater(structuredClone(row)) const newUnique = this.getUnique(newRow) if (oldUnique !== newUnique) { throw new Error( `Unique cannot be changed, before row: ${JSON.stringify(row)}, after row: ${JSON.stringify(newRow)}`, ) } this.internalSetRows([newRow]) return structuredClone(newRow) }) } /** * @description 按条件查找第一条行并更新;若未找到或唯一标识被改变则返回 undefined。 * * @returns { Promise } Updated row, or undefined if row not found, or unique * changed between before and after. */ async updateRow( getter: (row: RowType) => boolean, updater: (row: RowType) => RowType, ): Promise { try { return await this.updateRowOrThrow(getter, updater) } catch { return undefined } } /** * @description 按条件批量更新匹配到的全部行。 * * @returns { Promise } Updated rows. * * @throws { Error } If unique changed between before and after. */ async updateRows( getter: (row: RowType) => boolean, updater: (row: RowType) => RowType, ): Promise { return await this.mutex.runExclusive(() => { const rows = this.internalFindRows(getter) const updatedRows: RowType[] = [] for (const row of rows) { const oldUnique = this.getUnique(row) const newRow = updater(structuredClone(row)) const newUnique = this.getUnique(newRow) if (oldUnique !== newUnique) { throw new Error( `Unique cannot be changed, before row: ${JSON.stringify(row)}, after row: ${JSON.stringify(newRow)}`, ) } updatedRows.push(newRow) } this.internalSetRows(updatedRows) return structuredClone(updatedRows) }) } /** * @description 按唯一标识插入或浅合并一条行。 * * @returns { Promise } Upserted row. */ async upsertRow(newRow: RowType): Promise { const upserted = await this.mutex.runExclusive(() => { const unique = this.getUnique(newRow) const existRow = this.map.get(unique) if (existRow === undefined) { this.internalSetRows([newRow]) return structuredClone(newRow) } else { const updatedRow = { ...existRow, ...newRow } this.internalSetRows([updatedRow]) return structuredClone(updatedRow) } }) return upserted } /** * @description 按唯一标识批量插入或浅合并多条行。 * * @returns { Promise } Upserted rows. * * @throws { Error } If the batch contains duplicate uniques. */ async upsertRows(rows: RowType[]): Promise { return await this.mutex.runExclusive(() => { this.internalAssertBatchRowsHaveUniqueValues(rows) const upsertedRows: RowType[] = [] for (const row of rows) { const unique = this.getUnique(row) const existRow = this.map.get(unique) if (existRow === undefined) { upsertedRows.push(row) } else { upsertedRows.push({ ...existRow, ...row }) } } this.internalSetRows(upsertedRows) return structuredClone(upsertedRows) }) } /** * @description 按唯一标识安全删除一条行,并返回被删除的存量行。 * * @throws { Error } If row not found. */ async simpleDeleteRowOrThrow(row: RowType): Promise { return await this.mutex.runExclusive(() => { const unique = this.getUnique(row) const existRow = this.map.get(unique) if (existRow === undefined) { throw new Error(`Row not found, target row: ${JSON.stringify(row)}`) } this.internalUnsetRows([existRow]) return structuredClone(existRow) }) } /** * @description 按唯一标识删除一条行;若不存在则返回 undefined。 */ async simpleDeleteRow(row: RowType): Promise { try { return await this.simpleDeleteRowOrThrow(row) } catch { return undefined } } /** * @description 按唯一标识批量删除多条行,并返回被删除的存量行。 * * @throws { Error } If any row is not found or the batch contains duplicate uniques. */ async simpleDeleteRows(rows: RowType[]): Promise { return await this.mutex.runExclusive(() => { this.internalAssertBatchRowsHaveUniqueValues(rows) const toDeleteRows: RowType[] = [] for (const row of rows) { const unique = this.getUnique(row) const existRow = this.map.get(unique) if (existRow === undefined) { throw new Error(`Row not found, target row: ${JSON.stringify(row)}`) } toDeleteRows.push(existRow) } this.internalUnsetRows(toDeleteRows) return structuredClone(toDeleteRows) }) } /** * @description 按条件安全删除唯一一条匹配行。 * * @returns { Promise } Deleted row. * * @throws { Error } If multiple rows found, or row not found. */ async deleteRowOrThrow(getter: (row: RowType) => boolean): Promise { return await this.mutex.runExclusive(() => { const rows = this.internalFindRows(getter) if (rows.length > 1) { throw new Error(`Multiple rows found, target rows: ${JSON.stringify(rows)}`) } const row = rows[0] if (row === undefined) { throw new Error(`Row not found.`) } this.internalUnsetRows([row]) return structuredClone(row) }) } /** * @description 按条件删除唯一一条匹配行;若不存在则返回 undefined。 * * @returns { Promise } Deleted row, or undefined if row not found. * * @throws { Error } If multiple rows found. */ async deleteRow(getter: (row: RowType) => boolean): Promise { return await this.mutex.runExclusive(() => { const rows = this.internalFindRows(getter) if (rows.length > 1) { throw new Error(`Multiple rows found, target rows: ${JSON.stringify(rows)}`) } const row = rows[0] if (row === undefined) { return undefined } this.internalUnsetRows([row]) return structuredClone(row) }) } /** * @description 按条件删除全部匹配行。 * * @returns { Promise } Deleted rows. */ async deleteRows(getter: (row: RowType) => boolean): Promise { return await this.mutex.runExclusive(() => { const rows = this.internalFindRows(getter) this.internalUnsetRows(rows) return structuredClone(rows) }) } }