in packages/sqrl/src/ast/astIntersects.ts [58:129]
function extractFeatureValueSets(
ast: Ast
): {
[feature: string]: Set<boolean | string>;
} {
/***
* Returns a map of /either/
* feature: true, [for boolean features]
* feature: Set(...values), [ for equalities ]
*/
if (ast.type === "feature") {
return {
[ast.value]: booleanSet,
};
} else if (ast.type === "not") {
return extractFeatureValueSets(ast.expr);
} else if (ast.type === "boolean_expr") {
sqrlInvariant(
ast,
ast.operator === "and" || ast.operator === "or",
"Only and/or operators are allowed in where clause"
);
const results = {};
for (const sub of [ast.left, ast.right]) {
foreachObject(extractFeatureValueSets(sub), (values, feature) => {
if (!results.hasOwnProperty(feature)) {
results[feature] = values;
} else if (values === booleanSet) {
sqrlInvariant(
ast,
results[feature] === booleanSet,
"Invalid where: feature must be defined as boolean or equality"
);
} else {
sqrlInvariant(
ast,
results[feature] !== booleanSet,
"Invalid where: feature must be defined as boolean or equality"
);
values.forEach((value) => results[feature].add(value));
}
});
}
return results;
} else if (ast.type === "binary_expr") {
sqrlInvariant(ast, ["=", "!="].indexOf(ast.operator) >= 0, ast.operator);
if (ast.left.type !== "feature") {
throw buildSqrlError(
ast,
"Counter where clause equality left-hand side must be a feature"
);
}
if (ast.right.type !== "constant" || typeof ast.right.value !== "string") {
throw buildSqrlError(
ast,
"Counter where clause equality right-hand side must be a constant string"
);
}
return {
[ast.left.value]: new Set([ast.right.value]),
};
} else {
throw buildSqrlError(
ast,
"Only and/or/not and constant equality allowed in where clause"
);
}
}