use std::marker::PhantomData;
use std::ops::BitOr;
use serde::de::DeserializeOwned;
use serde_json::Value;
use crate::parser;
use crate::vm::VM;
use crate::Error;
#[derive(Debug, Clone)]
pub struct Expr<T> {
src: String,
_marker: PhantomData<fn() -> T>,
}
impl<T> Expr<T> {
pub fn new<S: Into<String>>(src: S) -> Result<Self, Error> {
let src = src.into();
parser::parse(&src)?;
Ok(Self { src, _marker: PhantomData })
}
pub fn as_str(&self) -> &str { &self.src }
pub fn into_string(self) -> String { self.src }
pub fn cast<U>(self) -> Expr<U> {
Expr { src: self.src, _marker: PhantomData }
}
}
impl<T: DeserializeOwned> Expr<T> {
pub fn eval(&self, doc: &Value) -> Result<T, Error> {
let raw = VM::new().run_str(&self.src, doc)?;
serde_json::from_value(raw).map_err(|e| Error::Eval(crate::EvalError(e.to_string())))
}
pub fn eval_with(&self, vm: &mut VM, doc: &Value) -> Result<T, Error> {
let raw = vm.run_str(&self.src, doc)?;
serde_json::from_value(raw).map_err(|e| Error::Eval(crate::EvalError(e.to_string())))
}
}
impl Expr<Value> {
pub fn eval_raw(&self, doc: &Value) -> Result<Value, Error> {
Ok(VM::new().run_str(&self.src, doc)?)
}
}
impl<T, U> BitOr<Expr<U>> for Expr<T> {
type Output = Expr<U>;
fn bitor(self, rhs: Expr<U>) -> Expr<U> {
Expr {
src: format!("({}) | ({})", self.src, rhs.src),
_marker: PhantomData,
}
}
}
impl<T> AsRef<str> for Expr<T> {
fn as_ref(&self) -> &str { &self.src }
}
impl<T> std::fmt::Display for Expr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.src)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn parse_ok() {
let e: Expr<i64> = Expr::new("$.x.len()").unwrap();
assert_eq!(e.as_str(), "$.x.len()");
}
#[test]
fn parse_err() {
let e: Result<Expr<i64>, _> = Expr::new("$$$ not valid");
assert!(e.is_err());
}
#[test]
fn eval_typed() {
let e: Expr<i64> = Expr::new("$.xs.len()").unwrap();
let n = e.eval(&json!({"xs": [1, 2, 3]})).unwrap();
assert_eq!(n, 3);
}
#[test]
fn eval_vec() {
let e: Expr<Vec<String>> = Expr::new("$.users.map(name)").unwrap();
let names = e.eval(&json!({
"users": [{"name":"a"}, {"name":"b"}]
})).unwrap();
assert_eq!(names, vec!["a", "b"]);
}
#[test]
fn pipe_compose() {
let a: Expr<Value> = Expr::new("$.books").unwrap();
let b: Expr<Vec<Value>> = Expr::new("@.filter(price > 10)").unwrap();
let piped = a | b;
assert_eq!(piped.as_str(), "($.books) | (@.filter(price > 10))");
}
#[test]
fn cast_keeps_src() {
let e: Expr<i64> = Expr::new("$.n").unwrap();
let s = e.clone().cast::<String>();
assert_eq!(s.as_str(), e.as_str());
}
}