pub mod ast;
pub mod engine;
pub mod eval;
pub mod expr;
pub mod graph;
pub mod parser;
pub mod vm;
pub mod analysis;
pub mod schema;
pub mod plan;
pub mod cfg;
pub mod ssa;
pub mod scan;
pub mod strref;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod examples;
use std::cell::{OnceCell, RefCell};
use std::sync::Arc;
use serde_json::Value;
use eval::Val;
pub use engine::Engine;
pub use eval::EvalError;
pub use eval::{Method, MethodRegistry, Val as JetroVal};
pub use expr::Expr;
pub use graph::Graph;
pub use parser::ParseError;
pub use vm::{VM, Compiler, Program};
pub trait JetroSchema {
const EXPRS: &'static [(&'static str, &'static str)];
fn exprs() -> &'static [(&'static str, &'static str)];
fn names() -> &'static [&'static str];
}
#[derive(Debug)]
pub enum Error {
Parse(ParseError),
Eval(EvalError),
}
pub type Result<T> = std::result::Result<T, Error>;
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Parse(e) => write!(f, "{}", e),
Error::Eval(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Parse(e) => Some(e),
Error::Eval(_) => None,
}
}
}
impl From<ParseError> for Error { fn from(e: ParseError) -> Self { Error::Parse(e) } }
impl From<EvalError> for Error { fn from(e: EvalError) -> Self { Error::Eval(e) } }
pub fn query(expr: &str, doc: &Value) -> Result<Value> {
let ast = parser::parse(expr)?;
Ok(eval::evaluate(&ast, doc)?)
}
pub fn query_with(expr: &str, doc: &Value, registry: Arc<MethodRegistry>) -> Result<Value> {
let ast = parser::parse(expr)?;
Ok(eval::evaluate_with(&ast, doc, registry)?)
}
thread_local! {
static THREAD_VM: RefCell<VM> = RefCell::new(VM::new());
}
pub struct Jetro {
document: Value,
root_val: OnceCell<Val>,
raw_bytes: Option<Arc<[u8]>>,
}
impl Jetro {
pub fn new(document: Value) -> Self {
Self { document, root_val: OnceCell::new(), raw_bytes: None }
}
pub fn from_bytes(bytes: Vec<u8>) -> std::result::Result<Self, serde_json::Error> {
let document: Value = serde_json::from_slice(&bytes)?;
Ok(Self {
document,
root_val: OnceCell::new(),
raw_bytes: Some(Arc::from(bytes.into_boxed_slice())),
})
}
pub fn from_slice(bytes: &[u8]) -> std::result::Result<Self, serde_json::Error> {
Self::from_bytes(bytes.to_vec())
}
fn root_val(&self) -> Val {
self.root_val.get_or_init(|| Val::from(&self.document)).clone()
}
pub fn collect<S: AsRef<str>>(&self, expr: S) -> std::result::Result<Value, EvalError> {
let expr = expr.as_ref();
THREAD_VM.with(|cell| match (cell.try_borrow_mut(), &self.raw_bytes) {
(Ok(mut vm), Some(bytes)) => {
let prog = vm.get_or_compile(expr)?;
vm.execute_val_with_raw(&prog, self.root_val(), Arc::clone(bytes))
}
(Ok(mut vm), None) => {
let prog = vm.get_or_compile(expr)?;
vm.execute_val(&prog, self.root_val())
}
(Err(_), Some(bytes)) => VM::new().run_str_with_raw(expr, &self.document, Arc::clone(bytes)),
(Err(_), None) => VM::new().run_str(expr, &self.document),
})
}
pub fn collect_val<S: AsRef<str>>(&self, expr: S) -> std::result::Result<JetroVal, EvalError> {
let expr = expr.as_ref();
THREAD_VM.with(|cell| {
let mut vm = cell.try_borrow_mut().map_err(|_| EvalError("VM in use".into()))?;
let prog = vm.get_or_compile(expr)?;
vm.execute_val_raw(&prog, self.root_val())
})
}
}
impl From<Value> for Jetro {
fn from(v: Value) -> Self {
Self::new(v)
}
}