/** * `filter=` — which members of a pool a row is allowed to draw from. * * The distinction that decides everything here, and the one to keep in the * documentation, is between this and `if=`: * * `if=` false → nothing is generated, the cell is empty. * `filter=` → a matching record is substituted; an empty cell never * happens, and "nobody matched" is an error rather than a gap. * * They also ask about different things. `if=` asks about the ROW — one answer * per row. `filter=` asks about each CANDIDATE — thirty answers per row for a * pool of thirty. That is why they are two attributes and can appear together: * * * * The expression is the ordinary one, evaluated in two scopes: a bare name is a * field of the candidate, anything else is a column of the current row. So * `price <= Budget` reads as "a product this customer can afford", which is * worth more than the clinic example that prompted the feature. * * One shape gets a fast path. `field == Column` (either way round) is the * common case and can be answered by bucketing the pool once, so a row costs a * map lookup instead of a scan over every member. Anything richer is a scan — * correct, and linear in the pool size, which is why the pool has a ceiling. */ import type { PoolTable } from './pool.js'; /** `field == Column` — the shape worth bucketing for. */ export interface EqualityFilter { /** Field of the pool member. */ readonly field: string; /** Column of the row it must equal. */ readonly column: string; } /** * Recognise `field == Column` / `Column == field`, and nothing else. * * Deliberately a string match rather than an AST inspection: the fast path must * be obvious to a reader of the config, not a surprise that appears when an * expression happens to normalise into the right shape. If it does not look * like the simple case, it is not treated as one. */ export declare function parseEqualityFilter(expr: string, table: PoolTable, isColumn: (name: string) => boolean): EqualityFilter | undefined; /** * member value → the members holding it. Built once per reference. * * Keyed by `matchKey` rather than by the raw text, so the bucket answers the * same question `==` would: a member holding `"01"` is found by a row producing * `"1"`, exactly as the general expression path finds it. */ export declare function bucketByField(table: PoolTable, field: string): Map; /** * The members of `table` for which `expr` holds on this row. * * `rowValue` reads a column of the current row; a name it does not know is left * for the evaluator to treat as a bare word, exactly as `if=` does. */ export declare function eligibleMembers(expr: string, table: PoolTable, rowValue: (name: string) => string | undefined, /** * Filled with the ROW columns the expression actually read, and what they held. * * The refusal for "nobody matched" used to name them only on the bucketed * `field == Column` path, so the general one told the author which filter * failed and never what the row was looking for — the one fact that says * whether the pool is missing a member or the filter is wrong. Recorded here * rather than parsed out of the expression: what the evaluator asked for IS * what the filter reads, including through `&&` and a ternary. */ readRowValues?: Map): number[]; /** ` (Clinic="North", Budget="40")` — what the row held, for the refusal below. */ export declare function rowValuesDetail(values: ReadonlyMap): string; /** The refusal a row gets when the filter leaves it with no member at all. */ export declare function noCandidateMessage(poolName: string, expr: string, row: number, detail: string): string; //# sourceMappingURL=pool-filter.d.ts.map