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