/**
* `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