1#![cfg_attr(feature = "specialized", feature(specialization))]
96
97pub use crate::errors::{ErrorReason, JmespathError, RuntimeError};
98pub use crate::parser::{ParseResult, parse};
99pub use crate::runtime::Runtime;
100pub use crate::variable::Variable;
101
102pub mod ast;
103pub mod functions;
104
105use serde::ser;
106#[cfg(feature = "specialized")]
107use serde_json::Value;
108#[cfg(feature = "specialized")]
109use std::convert::TryInto;
110use std::fmt;
111use std::sync::LazyLock;
112
113use crate::ast::Ast;
114use crate::interpreter::{SearchResult, interpret};
115
116mod errors;
117mod interpreter;
118mod lexer;
119mod parser;
120mod runtime;
121mod variable;
122
123pub static DEFAULT_RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
124 let mut runtime = Runtime::new();
125 runtime.register_builtin_functions();
126 runtime
127});
128
129#[cfg(not(feature = "sync"))]
131pub type Rcvar = std::rc::Rc<Variable>;
132#[cfg(feature = "sync")]
134pub type Rcvar = std::sync::Arc<Variable>;
135
136#[inline]
144pub fn compile(expression: &str) -> Result<Expression<'static>, JmespathError> {
145 DEFAULT_RUNTIME.compile(expression)
146}
147
148#[cfg_attr(
151 feature = "specialized",
152 doc = "\
153There is a generic serde Serialize implementation, and since this
154documentation was compiled with the `specialized` feature turned
155**on**, there are also a number of specialized implementations for
156`ToJmespath` built into the library that should work for most
157cases."
158)]
159#[cfg_attr(
160 not(feature = "specialized"),
161 doc = "\
162There is a generic serde Serialize implementation. Since this
163documentation was compiled with the `specialized` feature turned
164**off**, this is the only implementation available.
165
166(If the `specialized` feature were turned on, there there would be
167a number of additional specialized implementations for `ToJmespath`
168built into the library that should work for most cases.)"
169)]
170pub trait ToJmespath {
171 fn to_jmespath(self) -> Result<Rcvar, JmespathError>;
172}
173
174impl<T: ser::Serialize> ToJmespath for T {
176 #[cfg(not(feature = "specialized"))]
177 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
178 Variable::from_serializable(self).map(Rcvar::new)
179 }
180
181 #[cfg(feature = "specialized")]
182 default fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
183 Variable::from_serializable(self).map(Rcvar::new)
184 }
185}
186
187#[cfg(feature = "specialized")]
188impl ToJmespath for Value {
189 #[inline]
190 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
191 self.try_into().map(|var: Variable| Rcvar::new(var))
192 }
193}
194
195#[cfg(feature = "specialized")]
196impl ToJmespath for &Value {
197 #[inline]
198 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
199 self.try_into().map(|var: Variable| Rcvar::new(var))
200 }
201}
202
203#[cfg(feature = "specialized")]
204impl ToJmespath for Rcvar {
206 #[inline]
207 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
208 Ok(self)
209 }
210}
211
212#[cfg(feature = "specialized")]
213impl ToJmespath for &Rcvar {
214 #[inline]
215 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
216 Ok(self.clone())
217 }
218}
219
220#[cfg(feature = "specialized")]
221impl ToJmespath for Variable {
222 #[inline]
223 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
224 Ok(Rcvar::new(self))
225 }
226}
227
228#[cfg(feature = "specialized")]
229impl ToJmespath for &Variable {
230 #[inline]
231 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
232 Ok(Rcvar::new(self.clone()))
233 }
234}
235
236#[cfg(feature = "specialized")]
237impl ToJmespath for String {
238 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
239 Ok(Rcvar::new(Variable::String(self)))
240 }
241}
242
243#[cfg(feature = "specialized")]
244impl ToJmespath for &str {
245 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
246 Ok(Rcvar::new(Variable::String(self.to_owned())))
247 }
248}
249
250#[cfg(feature = "specialized")]
251impl ToJmespath for i8 {
252 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
253 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
254 }
255}
256
257#[cfg(feature = "specialized")]
258impl ToJmespath for i16 {
259 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
260 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
261 }
262}
263
264#[cfg(feature = "specialized")]
265impl ToJmespath for i32 {
266 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
267 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
268 }
269}
270
271#[cfg(feature = "specialized")]
272impl ToJmespath for i64 {
273 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
274 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
275 }
276}
277
278#[cfg(feature = "specialized")]
279impl ToJmespath for u8 {
280 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
281 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
282 }
283}
284
285#[cfg(feature = "specialized")]
286impl ToJmespath for u16 {
287 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
288 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
289 }
290}
291
292#[cfg(feature = "specialized")]
293impl ToJmespath for u32 {
294 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
295 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
296 }
297}
298
299#[cfg(feature = "specialized")]
300impl ToJmespath for u64 {
301 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
302 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
303 }
304}
305
306#[cfg(feature = "specialized")]
307impl ToJmespath for isize {
308 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
309 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
310 }
311}
312
313#[cfg(feature = "specialized")]
314impl ToJmespath for usize {
315 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
316 Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
317 }
318}
319
320#[cfg(feature = "specialized")]
321impl ToJmespath for f32 {
322 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
323 (self as f64).to_jmespath()
324 }
325}
326
327#[cfg(feature = "specialized")]
328impl ToJmespath for f64 {
329 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
330 Ok(Rcvar::new(Variable::Number(
331 serde_json::Number::from_f64(self).ok_or_else(|| {
332 JmespathError::new(
333 "",
334 0,
335 ErrorReason::Parse(format!("Cannot parse {self} into a Number")),
336 )
337 })?,
338 )))
339 }
340}
341
342#[cfg(feature = "specialized")]
343impl ToJmespath for () {
344 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
345 Ok(Rcvar::new(Variable::Null))
346 }
347}
348
349#[cfg(feature = "specialized")]
350impl ToJmespath for bool {
351 fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
352 Ok(Rcvar::new(Variable::Bool(self)))
353 }
354}
355
356#[derive(Clone)]
364pub struct Expression<'a> {
365 ast: Ast,
366 expression: String,
367 runtime: &'a Runtime,
368}
369
370impl<'a> Expression<'a> {
371 #[inline]
376 pub fn new<S>(expression: S, ast: Ast, runtime: &'a Runtime) -> Expression<'a>
377 where
378 S: Into<String>,
379 {
380 Expression {
381 expression: expression.into(),
382 ast,
383 runtime,
384 }
385 }
386
387 pub fn search<T: ToJmespath>(&self, data: T) -> SearchResult {
394 let mut ctx = Context::new(&self.expression, self.runtime);
395 interpret(&data.to_jmespath()?, &self.ast, &mut ctx)
396 }
397
398 pub fn as_str(&self) -> &str {
403 &self.expression
404 }
405
406 pub fn as_ast(&self) -> &Ast {
410 &self.ast
411 }
412}
413
414impl<'a> fmt::Display for Expression<'a> {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 write!(f, "{}", self.as_str())
418 }
419}
420
421impl<'a> fmt::Debug for Expression<'a> {
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 fmt::Display::fmt(self, f)
424 }
425}
426
427impl<'a> PartialEq for Expression<'a> {
428 fn eq(&self, other: &Expression<'_>) -> bool {
429 self.as_str() == other.as_str()
430 }
431}
432
433pub struct Context<'a> {
439 pub expression: &'a str,
441 pub runtime: &'a Runtime,
443 pub offset: usize,
445}
446
447impl<'a> Context<'a> {
448 #[inline]
450 pub fn new(expression: &'a str, runtime: &'a Runtime) -> Context<'a> {
451 Context {
452 expression,
453 runtime,
454 offset: 0,
455 }
456 }
457}
458
459#[cfg(test)]
460mod test {
461 use super::ast::Ast;
462 use super::*;
463
464 #[test]
465 fn formats_expression_as_string_or_debug() {
466 let expr = compile("foo | baz").unwrap();
467 assert_eq!("foo | baz/foo | baz", format!("{expr}/{expr:?}"));
468 }
469
470 #[test]
471 fn implements_partial_eq() {
472 let a = compile("@").unwrap();
473 let b = compile("@").unwrap();
474 assert!(a == b);
475 }
476
477 #[test]
478 fn can_evaluate_jmespath_expression() {
479 let expr = compile("foo.bar").unwrap();
480 let var = Variable::from_json("{\"foo\":{\"bar\":true}}").unwrap();
481 assert_eq!(Rcvar::new(Variable::Bool(true)), expr.search(var).unwrap());
482 }
483
484 #[test]
485 fn can_get_expression_ast() {
486 let expr = compile("foo").unwrap();
487 assert_eq!(
488 &Ast::Field {
489 offset: 0,
490 name: "foo".to_string(),
491 },
492 expr.as_ast()
493 );
494 }
495
496 #[test]
497 fn test_creates_rcvar_from_tuple_serialization() {
498 use super::ToJmespath;
499 let t = (true, false);
500 assert_eq!("[true,false]", t.to_jmespath().unwrap().to_string());
501 }
502
503 #[test]
504 fn expression_clone() {
505 let expr = compile("foo").unwrap();
506 let _ = expr.clone();
507 }
508
509 #[test]
510 fn test_invalid_number() {
511 let _ = compile("6455555524");
512 }
513}