import type { Address } from "./types"; import { isAddress } from "./types"; export const Any = "?"; class Var { constructor(public readonly name: string) {} } export function Variable(name: string): Var { return new Var(name); } type QueryPart = T | T[] | typeof Any | Var; export class Query { private _query: string | undefined; public static matches( entity: QueryPart, attribute: QueryPart, value: QueryPart ): Query { const query = new Query(); let entityStr; if (entity === Any) { entityStr = "?"; } else if (entity instanceof Var) { entityStr = `?${entity.name}`; } else { entityStr = Array.isArray(entity) ? `(in ${entity.join(" ")})` : entity; } let attributeStr; if (attribute === Any) { attributeStr = "?"; } else if (attribute instanceof Var) { attributeStr = `?${attribute.name}`; } else { attributeStr = Array.isArray(attribute) ? `(in ${attribute.map((a) => `"${a}"`).join(" ")})` : `"${attribute}"`; } let valueStr; if (value === Any) { valueStr = "?"; } else if (value instanceof Var) { valueStr = `?${value.name}`; } else { valueStr = (Array.isArray(value) ? value : [value]) .map((v) => { if (typeof v === "number") return v; if (isAddress(v)) return v; if (typeof v === "string") return `"${v}"`; }) .join(" "); valueStr = Array.isArray(value) ? `(in ${valueStr})` : valueStr; } query._query = `(matches ${entityStr} ${attributeStr} ${valueStr})`; return query; } public static or(...queries: Query[]): Query { const query = new Query(); query._query = `(or ${queries.join(" ")})`; return query; } public static and(...queries: Query[]): Query { const query = new Query(); query._query = `(and ${queries.join(" ")})`; return query; } public static not(query: Query): Query { const q = new Query(); q._query = `(not ${query})`; return q; } public static join(...queries: Query[]): Query { const query = new Query(); query._query = `(join ${queries.join(" ")})`; return query; } public toString(): string { if (!this._query) throw new Error("Query is not defined"); return this._query; } }