#![cfg_attr(feature = "specialized", feature(specialization))]
pub use crate::errors::{ErrorReason, JmespathError, RuntimeError};
pub use crate::parser::{parse, ParseResult};
pub use crate::runtime::Runtime;
pub use crate::variable::Variable;
pub mod ast;
pub mod functions;
use serde::ser;
#[cfg(feature = "specialized")]
use serde_json::Value;
#[cfg(feature = "specialized")]
use std::convert::TryInto;
use std::fmt;
use lazy_static::*;
use crate::ast::Ast;
use crate::interpreter::{interpret, SearchResult};
mod errors;
mod interpreter;
mod lexer;
mod parser;
mod runtime;
mod variable;
lazy_static! {
pub static ref DEFAULT_RUNTIME: Runtime = {
let mut runtime = Runtime::new();
runtime.register_builtin_functions();
runtime
};
}
#[cfg(not(feature = "sync"))]
pub type Rcvar = std::rc::Rc<Variable>;
#[cfg(feature = "sync")]
pub type Rcvar = std::sync::Arc<Variable>;
#[inline]
pub fn compile(expression: &str) -> Result<Expression<'static>, JmespathError> {
DEFAULT_RUNTIME.compile(expression)
}
#[cfg_attr(
feature = "specialized",
doc = "\
There is a generic serde Serialize implementation, and since this
documentation was compiled with the `specialized` feature turned
**on**, there are also a number of specialized implementations for
`ToJmespath` built into the library that should work for most
cases."
)]
#[cfg_attr(
not(feature = "specialized"),
doc = "\
There is a generic serde Serialize implementation. Since this
documentation was compiled with the `specialized` feature turned
**off**, this is the only implementation available.
(If the `specialized` feature were turned on, there there would be
a number of additional specialized implementations for `ToJmespath`
built into the library that should work for most cases.)"
)]
pub trait ToJmespath {
fn to_jmespath(self) -> Result<Rcvar, JmespathError>;
}
impl<'a, T: ser::Serialize> ToJmespath for T {
#[cfg(not(feature = "specialized"))]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Variable::from_serializable(self).map(Rcvar::new)?)
}
#[cfg(feature = "specialized")]
default fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Variable::from_serializable(self).map(|var| Rcvar::new(var))?)
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for Value {
#[inline]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
self.try_into().map(|var: Variable| Rcvar::new(var))
}
}
#[cfg(feature = "specialized")]
impl<'a> ToJmespath for &'a Value {
#[inline]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
self.try_into().map(|var: Variable| Rcvar::new(var))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for Rcvar {
#[inline]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(self)
}
}
#[cfg(feature = "specialized")]
impl<'a> ToJmespath for &'a Rcvar {
#[inline]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(self.clone())
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for Variable {
#[inline]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(self))
}
}
#[cfg(feature = "specialized")]
impl<'a> ToJmespath for &'a Variable {
#[inline]
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(self.clone()))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for String {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::String(self)))
}
}
#[cfg(feature = "specialized")]
impl<'a> ToJmespath for &'a str {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::String(self.to_owned())))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for i8 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for i16 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for i32 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for i64 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for u8 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for u16 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for u32 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for u64 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for isize {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for usize {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(serde_json::Number::from(self))))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for f32 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
(self as f64).to_jmespath()
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for f64 {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Number(
serde_json::Number::from_f64(self).ok_or_else(|| {
JmespathError::new(
"",
0,
ErrorReason::Parse(format!("Cannot parse {} into a Number", self)),
)
})?,
)))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for () {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Null))
}
}
#[cfg(feature = "specialized")]
impl ToJmespath for bool {
fn to_jmespath(self) -> Result<Rcvar, JmespathError> {
Ok(Rcvar::new(Variable::Bool(self)))
}
}
#[derive(Clone)]
pub struct Expression<'a> {
ast: Ast,
expression: String,
runtime: &'a Runtime,
}
impl<'a> Expression<'a> {
#[inline]
pub fn new<S>(expression: S, ast: Ast, runtime: &'a Runtime) -> Expression<'a>
where
S: Into<String>,
{
Expression {
expression: expression.into(),
ast,
runtime,
}
}
pub fn search<T: ToJmespath>(&self, data: T) -> SearchResult {
let mut ctx = Context::new(&self.expression, self.runtime);
interpret(&data.to_jmespath()?, &self.ast, &mut ctx)
}
pub fn as_str(&self) -> &str {
&self.expression
}
pub fn as_ast(&self) -> &Ast {
&self.ast
}
}
impl<'a> fmt::Display for Expression<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl<'a> fmt::Debug for Expression<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<'a> PartialEq for Expression<'a> {
fn eq(&self, other: &Expression<'_>) -> bool {
self.as_str() == other.as_str()
}
}
pub struct Context<'a> {
pub expression: &'a str,
pub runtime: &'a Runtime,
pub offset: usize,
}
impl<'a> Context<'a> {
#[inline]
pub fn new(expression: &'a str, runtime: &'a Runtime) -> Context<'a> {
Context {
expression,
runtime,
offset: 0,
}
}
}
#[cfg(test)]
mod test {
use super::ast::Ast;
use super::*;
#[test]
fn formats_expression_as_string_or_debug() {
let expr = compile("foo | baz").unwrap();
assert_eq!("foo | baz/foo | baz", format!("{}/{:?}", expr, expr));
}
#[test]
fn implements_partial_eq() {
let a = compile("@").unwrap();
let b = compile("@").unwrap();
assert!(a == b);
}
#[test]
fn can_evaluate_jmespath_expression() {
let expr = compile("foo.bar").unwrap();
let var = Variable::from_json("{\"foo\":{\"bar\":true}}").unwrap();
assert_eq!(Rcvar::new(Variable::Bool(true)), expr.search(var).unwrap());
}
#[test]
fn can_get_expression_ast() {
let expr = compile("foo").unwrap();
assert_eq!(
&Ast::Field {
offset: 0,
name: "foo".to_string(),
},
expr.as_ast()
);
}
#[test]
fn test_creates_rcvar_from_tuple_serialization() {
use super::ToJmespath;
let t = (true, false);
assert_eq!("[true,false]", t.to_jmespath().unwrap().to_string());
}
#[test]
fn expression_clone() {
let expr = compile("foo").unwrap();
let _ = expr.clone();
}
}