1pub mod ast;
20#[cfg(feature = "extensions")]
21pub mod extensions;
22pub mod functions;
23pub mod query_library;
24pub mod registry;
25pub mod value_ext;
26
27pub use crate::error::{ErrorReason, JmespathError, RuntimeError};
28pub use crate::interpreter::SearchResult;
29pub use crate::parser::{ParseResult, parse};
30pub use crate::registry::{Category, Feature, FunctionInfo, FunctionRegistry};
31pub use crate::runtime::{Runtime, RuntimeBuilder};
32pub use crate::value_ext::{JmespathType, ValueExt};
33
34mod error;
35pub mod interpreter;
36mod lexer;
37mod parser;
38mod runtime;
39
40#[cfg(feature = "let-expr")]
41use std::collections::HashMap;
42use std::fmt;
43use std::sync::LazyLock;
44
45use serde_json::Value;
46
47use crate::ast::Ast;
48use crate::interpreter::interpret;
49
50pub static DEFAULT_RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
52 let mut runtime = Runtime::new();
53 runtime.register_builtin_functions();
54 runtime
55});
56
57#[inline]
59pub fn compile(expression: &str) -> Result<Expression<'static>, JmespathError> {
60 DEFAULT_RUNTIME.compile(expression)
61}
62
63#[derive(Clone)]
68pub struct Expression<'a> {
69 ast: Ast,
70 expression: String,
71 runtime: &'a Runtime,
72}
73
74impl<'a> Expression<'a> {
75 #[inline]
77 pub fn new<S>(expression: S, ast: Ast, runtime: &'a Runtime) -> Expression<'a>
78 where
79 S: Into<String>,
80 {
81 Expression {
82 expression: expression.into(),
83 ast,
84 runtime,
85 }
86 }
87
88 pub fn search(&self, data: &Value) -> SearchResult {
92 let mut ctx = Context::new(&self.expression, self.runtime);
93 let result = interpret(data, &self.ast, &mut ctx)?;
94 Ok(strip_expref_sentinels(result))
96 }
97
98 pub fn as_str(&self) -> &str {
100 &self.expression
101 }
102
103 pub fn as_ast(&self) -> &Ast {
105 &self.ast
106 }
107}
108
109impl<'a> fmt::Display for Expression<'a> {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "{}", self.as_str())
112 }
113}
114
115impl<'a> fmt::Debug for Expression<'a> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 fmt::Display::fmt(self, f)
118 }
119}
120
121impl<'a> PartialEq for Expression<'a> {
122 fn eq(&self, other: &Expression<'_>) -> bool {
123 self.as_str() == other.as_str()
124 }
125}
126
127pub struct Context<'a> {
133 pub expression: &'a str,
135 pub runtime: &'a Runtime,
137 pub offset: usize,
139 pub(crate) expref_table: Vec<Ast>,
142 #[cfg(feature = "let-expr")]
144 scopes: Vec<HashMap<String, Value>>,
145}
146
147impl<'a> Context<'a> {
148 #[inline]
150 pub fn new(expression: &'a str, runtime: &'a Runtime) -> Context<'a> {
151 Context {
152 expression,
153 runtime,
154 offset: 0,
155 expref_table: Vec::new(),
156 #[cfg(feature = "let-expr")]
157 scopes: Vec::new(),
158 }
159 }
160
161 pub(crate) fn store_expref(&mut self, ast: Ast) -> usize {
163 let id = self.expref_table.len();
164 self.expref_table.push(ast);
165 id
166 }
167
168 pub fn get_expref(&self, id: usize) -> Option<&Ast> {
170 self.expref_table.get(id)
171 }
172
173 #[cfg(feature = "let-expr")]
175 #[inline]
176 pub fn push_scope(&mut self, bindings: HashMap<String, Value>) {
177 self.scopes.push(bindings);
178 }
179
180 #[cfg(feature = "let-expr")]
182 #[inline]
183 pub fn pop_scope(&mut self) {
184 self.scopes.pop();
185 }
186
187 #[cfg(feature = "let-expr")]
189 #[inline]
190 pub fn get_variable(&self, name: &str) -> Option<Value> {
191 for scope in self.scopes.iter().rev() {
192 if let Some(value) = scope.get(name) {
193 return Some(value.clone());
194 }
195 }
196 None
197 }
198}
199
200pub(crate) fn make_expref_sentinel(id: usize) -> Value {
202 let mut map = serde_json::Map::new();
203 map.insert(
204 "__jpx_expref__".to_string(),
205 Value::Number(serde_json::Number::from(id)),
206 );
207 Value::Object(map)
208}
209
210pub fn get_expref_id(value: &Value) -> Option<usize> {
212 value
213 .as_object()
214 .and_then(|m| m.get("__jpx_expref__"))
215 .and_then(|v| v.as_u64())
216 .map(|v| v as usize)
217}
218
219fn strip_expref_sentinels(value: Value) -> Value {
221 match value {
222 Value::Object(map) if map.contains_key("__jpx_expref__") => Value::Null,
223 Value::Array(arr) => Value::Array(arr.into_iter().map(strip_expref_sentinels).collect()),
224 Value::Object(map) => Value::Object(
225 map.into_iter()
226 .map(|(k, v)| (k, strip_expref_sentinels(v)))
227 .collect(),
228 ),
229 other => other,
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use serde_json::json;
237
238 #[test]
239 fn formats_expression_as_string_or_debug() {
240 let expr = compile("foo | baz").unwrap();
241 assert_eq!("foo | baz/foo | baz", format!("{expr}/{expr:?}"));
242 }
243
244 #[test]
245 fn implements_partial_eq() {
246 let a = compile("@").unwrap();
247 let b = compile("@").unwrap();
248 assert!(a == b);
249 }
250
251 #[test]
252 fn can_evaluate_jmespath_expression() {
253 let expr = compile("foo.bar").unwrap();
254 let data = json!({"foo": {"bar": true}});
255 assert_eq!(json!(true), expr.search(&data).unwrap());
256 }
257
258 #[test]
259 fn can_get_expression_ast() {
260 let expr = compile("foo").unwrap();
261 assert_eq!(
262 &Ast::Field {
263 offset: 0,
264 name: "foo".to_string(),
265 },
266 expr.as_ast()
267 );
268 }
269
270 #[test]
271 fn expression_clone() {
272 let expr = compile("foo").unwrap();
273 let _ = expr.clone();
274 }
275
276 #[test]
277 fn test_invalid_number() {
278 let _ = compile("6455555524");
279 }
280}
281
282#[cfg(all(test, feature = "let-expr"))]
283mod let_tests {
284 use super::*;
285 use serde_json::json;
286
287 #[test]
288 fn test_simple_let_expression() {
289 let expr = compile("let $x = `1` in $x").unwrap();
290 let data = json!({});
291 let result = expr.search(&data).unwrap();
292 assert_eq!(result, json!(1));
293 }
294
295 #[test]
296 fn test_let_with_data_reference() {
297 let expr = compile("let $name = name in $name").unwrap();
298 let data = json!({"name": "Alice"});
299 let result = expr.search(&data).unwrap();
300 assert_eq!(result, json!("Alice"));
301 }
302
303 #[test]
304 fn test_let_multiple_bindings() {
305 let expr = compile("let $a = `1`, $b = `2` in [$a, $b]").unwrap();
306 let data = json!({});
307 let result = expr.search(&data).unwrap();
308 assert_eq!(result, json!([1, 2]));
309 }
310
311 #[test]
312 fn test_let_with_expression_body() {
313 let expr = compile("let $items = items in $items[0].name").unwrap();
314 let data = json!({"items": [{"name": "first"}, {"name": "second"}]});
315 let result = expr.search(&data).unwrap();
316 assert_eq!(result, json!("first"));
317 }
318
319 #[test]
320 fn test_nested_let() {
321 let expr = compile("let $x = `1` in let $y = `2` in [$x, $y]").unwrap();
322 let data = json!({});
323 let result = expr.search(&data).unwrap();
324 assert_eq!(result, json!([1, 2]));
325 }
326
327 #[test]
328 fn test_let_variable_shadowing() {
329 let expr = compile("let $x = `1` in let $x = `2` in $x").unwrap();
330 let data = json!({});
331 let result = expr.search(&data).unwrap();
332 assert_eq!(result, json!(2));
333 }
334
335 #[test]
336 fn test_undefined_variable_error() {
337 let expr = compile("$undefined").unwrap();
338 let data = json!({});
339 let result = expr.search(&data);
340 assert!(result.is_err());
341 }
342
343 #[test]
344 fn test_let_in_projection() {
345 let expr = compile("let $threshold = `50` in numbers[? @ > $threshold]").unwrap();
346 let data = json!({"numbers": [10, 30, 50, 70, 90]});
347 let result = expr.search(&data).unwrap();
348 assert_eq!(result, json!([70, 90]));
349 }
350
351 #[test]
352 fn test_let_variable_used_multiple_times() {
353 let expr = compile("let $foo = foo.bar in [$foo, $foo]").unwrap();
354 let data = json!({"foo": {"bar": "baz"}});
355 let result = expr.search(&data).unwrap();
356 assert_eq!(result, json!(["baz", "baz"]));
357 }
358
359 #[test]
360 fn test_let_shadowing_in_projection() {
361 let expr = compile("let $a = a in b[*].[a, $a, let $a = 'shadow' in $a]").unwrap();
362 let data = json!({"a": "topval", "b": [{"a": "inner1"}, {"a": "inner2"}]});
363 let result = expr.search(&data).unwrap();
364 assert_eq!(
365 result,
366 json!([
367 ["inner1", "topval", "shadow"],
368 ["inner2", "topval", "shadow"]
369 ])
370 );
371 }
372
373 #[test]
374 fn test_let_bindings_evaluated_in_outer_scope() {
375 let expr = compile("let $a = 'top-a' in let $a = 'in-a', $b = $a in $b").unwrap();
376 let data = json!({});
377 let result = expr.search(&data).unwrap();
378 assert_eq!(result, json!("top-a"));
379 }
380
381 #[test]
382 fn test_let_projection_stopping() {
383 let expr = compile("let $foo = foo[*] in $foo[0]").unwrap();
384 let data = json!({"foo": [[0, 1], [2, 3], [4, 5]]});
385 let result = expr.search(&data).unwrap();
386 assert_eq!(result, json!([0, 1]));
387 }
388
389 #[test]
390 fn test_let_shadow_and_restore() {
391 let expr = compile("let $x = 'outer' in [let $x = 'inner' in $x, $x]").unwrap();
392 let data = json!({});
393 let result = expr.search(&data).unwrap();
394 assert_eq!(result, json!(["inner", "outer"]));
395 }
396
397 #[test]
398 fn test_let_with_functions() {
399 let expr = compile("let $arr = numbers in length($arr)").unwrap();
400 let data = json!({"numbers": [1, 2, 3, 4, 5]});
401 let result = expr.search(&data).unwrap();
402 assert_eq!(result, json!(5));
403 }
404
405 #[test]
406 fn test_let_deeply_nested_scopes() {
407 let expr = compile("let $a = `1` in let $b = `2` in let $c = `3` in [$a, $b, $c]").unwrap();
408 let data = json!({});
409 let result = expr.search(&data).unwrap();
410 assert_eq!(result, json!([1, 2, 3]));
411 }
412
413 #[test]
414 fn test_let_with_flatten() {
415 let expr = compile("let $data = nested in $data[].items[]").unwrap();
416 let data = json!({"nested": [{"items": [1, 2]}, {"items": [3, 4]}]});
417 let result = expr.search(&data).unwrap();
418 assert_eq!(result, json!([1, 2, 3, 4]));
419 }
420
421 #[test]
422 fn test_let_with_slice() {
423 let expr = compile("let $arr = numbers in $arr[1:3]").unwrap();
424 let data = json!({"numbers": [0, 1, 2, 3, 4]});
425 let result = expr.search(&data).unwrap();
426 assert_eq!(result, json!([1, 2]));
427 }
428
429 #[test]
430 fn test_let_with_or_expression() {
431 let expr = compile("let $default = 'N/A' in name || $default").unwrap();
432 let data = json!({});
433 let result = expr.search(&data).unwrap();
434 assert_eq!(result, json!("N/A"));
435 }
436
437 #[test]
438 fn test_let_with_not_expression() {
439 let expr = compile("let $val = `false` in !$val").unwrap();
440 let data = json!({});
441 let result = expr.search(&data).unwrap();
442 assert_eq!(result, json!(true));
443 }
444
445 #[test]
446 fn test_let_binding_to_literal() {
447 let expr = compile(
448 "let $str = 'hello', $num = `42`, $bool = `true`, $null = `null` in [$str, $num, $bool, $null]",
449 )
450 .unwrap();
451 let data = json!({});
452 let result = expr.search(&data).unwrap();
453 assert_eq!(result, json!(["hello", 42, true, null]));
454 }
455}