export type DistinguisherParser = (distinguisher: string) => T; export type DocumentParser = (document: string[]) => T; const LEADING_WHITESPACE = /^([ \t]+)/; const ENTRY = /^(.+?):\s*(.*)/; export class Idv { collections: Record = {}; public static parse(input: string): Idv { const lines = input.split("\n").map((line) => line.trimEnd()); return Idv.parseLines(lines); } static parseLines(input: string[]): Idv { const idv = new Idv(); let currentDocument: string[] = []; input.forEach((line) => { const indent = LEADING_WHITESPACE.exec(line)?.[1]; if (indent) { // TODO } else if (line == "") { // TODO } else if (line[0] == "#") { // skip } else { const matches = ENTRY.exec(line); if (matches) { const [, collection, distinguisher] = matches; if (idv.collections[collection] == undefined) { idv.collections[collection] = []; } currentDocument = []; idv.collections[collection].push([distinguisher, currentDocument]); } else { throw new Error("Failed to parse a property"); } } }); return idv; } public getProperty( name: string, parseDistinguisher: DistinguisherParser, parseDocument: DocumentParser ): T | null { const firstEntry = this.collections[name]?.[0]; return firstEntry && firstEntry[1].length > 0 ? parseDocument(firstEntry[1]) : firstEntry?.[0] ? parseDistinguisher(firstEntry[0]) : null; } public getList( name: string, parseDistinguisher: DistinguisherParser, parseDocument: DocumentParser ): T[] { return (this.collections[name] ?? []).map(([distinguisher, document]) => document.length > 0 ? parseDocument(document) : parseDistinguisher(distinguisher) ); } } export const StringFromDocument = (lines: string[]) => lines.join("\n");