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;
33mod distance;
34mod error;
35mod eval;
36mod ext;
37mod geometry;
38mod parse;
39mod typ;
40mod typecheck;
41mod value;
42
43pub use ast::{Expr, InterpKind, InterpSpace};
44pub use color::Color;
45pub use context::{EvaluationContext, Feature};
46pub use error::{EvalError, EvalErrorKind, ParseError, ParseErrorKind};
47pub use ext::{Function, Macro, Options};
48pub use typ::Type;
49pub use value::{Projection, Value};
50
51/// Parse a MapLibre expression from its JSON representation.
52pub fn parse(json: &serde_json::Value) -> Result<Expr, ParseError> {
53 parse::parse(json, &Options::default())
54}
55
56/// Parse an expression with user [`Options`] (macros expand at parse time;
57/// function names are accepted as callable operators).
58pub fn parse_with(json: &serde_json::Value, options: &Options) -> Result<Expr, ParseError> {
59 parse::parse(json, options)
60}
61
62/// Statically type-check a parsed expression, optionally against the type a
63/// property expects. On success returns a rewritten tree with type-directed
64/// coercion/assertion nodes inserted (evaluate this returned expression so the
65/// coercions take effect). Returns a [`ParseError`] for expressions the
66/// reference implementation rejects at compile time (bad comparisons, malformed
67/// `match` branches, non-interpolatable outputs, misused `zoom`, and so on).
68///
69/// `coerce_top_string` reflects whether the target property is string-typed
70/// (not merely enum-typed): such properties coerce the top-level result to a
71/// string rather than asserting it.
72pub fn typecheck(
73 expr: &Expr,
74 expected: Option<&Type>,
75 coerce_top_string: bool,
76) -> Result<Expr, ParseError> {
77 typecheck::typecheck(expr, expected, coerce_top_string)
78}
79
80/// Evaluate a parsed expression against an evaluation context.
81pub fn evaluate(expr: &Expr, ctx: &EvaluationContext) -> Result<Value, EvalError> {
82 eval::eval(expr, ctx)
83}
84
85/// Evaluate with user [`Options`], so calls to user functions are dispatched to
86/// their (recursion-limited) bodies.
87pub fn evaluate_with(
88 expr: &Expr,
89 ctx: &EvaluationContext,
90 options: &Options,
91) -> Result<Value, EvalError> {
92 eval::eval_with(expr, ctx, options)
93}