ravenrs_criteria/lib.rs
1//! A criteria and expression language, independent of any UI: the same
2//! expressions filter a grid, compute a report total, guard a security rule or
3//! become a SQL `WHERE`.
4//!
5//! * [`Criteria`] is a public tree. Parse it from text, build it in code, walk
6//! it, and print it back to text that parses to the same tree.
7//! * [`Scope`] evaluates it in memory over any [`Record`]: JSON, a map, or your
8//! own rows.
9//! * [`validate`] checks it against a [`Schema`] before it runs.
10//! * [`to_sql`] translates it to parameterized SQL.
11//! * [`functions::catalog`] lists every function, for expression editors.
12//!
13//! # Syntax
14//!
15//! * Fields and parameters: `[Price]`, `[Customer.Name]`, `[^.Number]` (parent row), `?MinTotal`.
16//! * Literals: `12.5`, `'text'` (`''` escapes a quote), `#2026-11-01#`, `True`, `False`, `Null`, `{guid}`.
17//! * Operators:
18//! * Arithmetic: `+ - * / %`; `+` joins text.
19//! * Comparison: `= <> != < <= > >=`, `Like '%a_'`.
20//! * Logic: `And Or Not` (or `&& || !`).
21//! * Bits: `& | ^ ~`.
22//! * Tests: `In (a, b)`, `Between(a, b)`, `Is Null`, `Is Not Null`, `Not In (...)`.
23//! * Functions: `Iif(...)`, `Upper(...)`, `AddMonths(...)` and others, in any case.
24//! * Aggregates over the rows in scope: `Sum([Total])`, `Count()`, also `sumSum(...)`.
25//! * Aggregates over a nested collection, optionally filtered:
26//! `[Items][].Sum([Qty] * [Price])`, `[Items][[Qty] > 0].Count()`.
27//! * Comments: `/* like this */`.
28//!
29//! ```
30//! use ravenrs_criteria::{Criteria, Environment, Scope, Value};
31//! use serde_json::json;
32//!
33//! let order = json!({ "Status": "open", "Items": [{ "Qty": 2, "Price": 5.0 }, { "Qty": 0, "Price": 9.0 }] });
34//! let total = Criteria::parse("[Items][[Qty] > 0].Sum([Qty] * [Price])").expect("valid");
35//! let env = Environment::new();
36//! assert_eq!(Scope::new(&env).row(&order).evaluate(&total), Ok(Value::Number(10.0)));
37//!
38//! let filter = Criteria::parse("[Status] = ?Wanted").expect("valid");
39//! let env = Environment::new().parameter("Wanted", "open");
40//! assert!(Scope::new(&env).row(&order).matches(&filter));
41//! ```
42
43mod ast;
44mod eval;
45mod format;
46pub mod functions;
47mod parse;
48mod sql;
49mod validate;
50mod value;
51
52pub use ast::{AggregateCall, BinaryOp, Criteria, GroupOp, UnaryOp};
53pub use eval::{CustomFunction, Environment, EvalError, Record, Scope};
54pub use format::{Culture, NumberStyle, format_composite, format_value};
55pub use functions::{Aggregate, Category, Function, FunctionInfo};
56pub use parse::ParseError;
57pub use sql::{Dialect, SqlError, SqlParam, SqlQuery, to_sql};
58pub use validate::{FieldType, Problem, Schema, validate};
59pub use value::{DateTime, Value};