maplibre_expr/lib.rs
1//! `maplibre_expr` — a pure-Rust parser and evaluator for
2//! [MapLibre GL style expressions][spec].
3//!
4//! The crate turns a MapLibre expression (a `serde_json::Value` such as
5//! `["*", ["get", "x"], 2]`) into an [`Expr`] tree with [`parse`], then
6//! evaluates that tree against an [`EvaluationContext`] with [`evaluate`].
7//!
8//! ```
9//! use maplibre_expr::{parse, evaluate, EvaluationContext, Feature, Value};
10//! use std::collections::BTreeMap;
11//!
12//! let json: serde_json::Value = serde_json::json!(["*", ["get", "x"], 2]);
13//! let expr = parse(&json).unwrap();
14//!
15//! let mut props = BTreeMap::new();
16//! props.insert("x".to_string(), Value::Number(21.0));
17//! let ctx = EvaluationContext::new().with_feature(Feature {
18//! properties: props,
19//! ..Feature::default()
20//! });
21//!
22//! assert_eq!(evaluate(&expr, &ctx).unwrap(), Value::Number(42.0));
23//! ```
24//!
25//! Conformance is validated against a vendored snapshot of the
26//! `maplibre-style-spec` expression test fixtures; see `tests/spec.rs`.
27//!
28//! [spec]: https://maplibre.org/maplibre-style-spec/expressions/
29
30mod ast;
31mod color;
32mod context;
33pub mod convert;
34mod distance;
35mod error;
36mod eval;
37mod ext;
38pub mod filter;
39mod geometry;
40mod parse;
41mod typ;
42mod typecheck;
43mod value;
44
45pub use ast::{Expr, InterpKind, InterpSpace};
46pub use color::Color;
47pub use context::{EvaluationContext, Feature};
48pub use error::{EvalError, EvalErrorKind, ParseError, ParseErrorKind};
49pub use ext::{Function, Macro, Options};
50pub use filter::{convert_legacy_filter, is_expression_filter, FilterError};
51pub use typ::Type;
52pub use value::{Projection, Value};
53
54/// Parse a MapLibre expression from its JSON representation.
55pub fn parse(json: &serde_json::Value) -> Result<Expr, ParseError> {
56 parse::parse(json, &Options::default())
57}
58
59/// Parse an expression with user [`Options`] (macros expand at parse time;
60/// function names are accepted as callable operators).
61pub fn parse_with(json: &serde_json::Value, options: &Options) -> Result<Expr, ParseError> {
62 parse::parse(json, options)
63}
64
65/// Statically type-check a parsed expression, optionally against the type a
66/// property expects. On success returns a rewritten tree with type-directed
67/// coercion/assertion nodes inserted (evaluate this returned expression so the
68/// coercions take effect). Returns a [`ParseError`] for expressions the
69/// reference implementation rejects at compile time (bad comparisons, malformed
70/// `match` branches, non-interpolatable outputs, misused `zoom`, and so on).
71///
72/// `coerce_top_string` reflects whether the target property is string-typed
73/// (not merely enum-typed): such properties coerce the top-level result to a
74/// string rather than asserting it.
75pub fn typecheck(
76 expr: &Expr,
77 expected: Option<&Type>,
78 coerce_top_string: bool,
79) -> Result<Expr, ParseError> {
80 typecheck::typecheck(expr, expected, coerce_top_string)
81}
82
83/// Evaluate a parsed expression against an evaluation context.
84pub fn evaluate(expr: &Expr, ctx: &EvaluationContext) -> Result<Value, EvalError> {
85 eval::eval(expr, ctx)
86}
87
88/// Evaluate with user [`Options`], so calls to user functions are dispatched to
89/// their (recursion-limited) bodies.
90pub fn evaluate_with(
91 expr: &Expr,
92 ctx: &EvaluationContext,
93 options: &Options,
94) -> Result<Value, EvalError> {
95 eval::eval_with(expr, ctx, options)
96}