evalx 0.5.0

Expression evaluator
Documentation
use crate::{Function, Functions, Context, Contexts, Compiled, Value};
use crate::tree::Tree;
use crate::error::Error;
use serde::Serialize;
use crate::to_value;
use std::fmt;


/// Expression builder
pub struct Expr {
    expression: String,
    compiled: Option<Compiled>,
    functions: Functions,
    contexts: Contexts,
}

impl Expr {
    /// Create an expression.
    pub fn new<T: Into<String>>(expr: T) -> Self {
        Self {
            expression: expr.into(),
            compiled: None,
            functions: Functions::new(),
            contexts: create_empty_contexts(),
        }
    }

    /// Set function.
    pub fn function<T, F>(mut self, name: T, function: F) -> Self
        where T: Into<String>,
              F: 'static + Fn(Vec<Value>) -> Result<Value, Error> + Sync + Send
    {
        self.functions.insert(name.into(), Function::new(function));
        self
    }

    /// Set value.
    pub fn value<T, V>(mut self, name: T, value: V) -> Self
        where T: Into<String>,
              V: Serialize
    {
        self.contexts.last_mut().unwrap().insert(name.into(), to_value(value));
        self
    }

    /// Compile an expression.
    /// An expression can be compiled only once and then invoked multiple times with different context and function.
    /// You can also execute a expression without compile.
    pub fn compile(mut self) -> Result<Self, Error> {
        self.compiled = Some(Tree::new(self.expression.clone()).compile()?);
        Ok(self)
    }

    /// Execute the expression.
    pub fn exec(&self) -> Result<Value, Error> {
        if let Some(compiled) = self.compiled.as_ref() {
            compiled(&self.contexts, &self.functions)
        } else {
            Tree::new(self.expression.clone()).compile()?(&self.contexts, &self.functions)
        }
    }

    fn get_compiled(&self) -> Option<&Compiled> {
        self.compiled.as_ref()
    }
}

impl Clone for Expr {
    /// Returns a copy of the value. Notice that functions can not be cloned. The cloned expr's functions will be empty.
    fn clone(&self) -> Self {
        Self {
            expression: self.expression.clone(),
            compiled: if self.compiled.is_some() {
                Some(Tree::new(self.expression.clone()).compile().unwrap())
            } else {
                None
            },
            contexts: self.contexts.clone(),
            functions: Functions::new(),
        }
    }
}

impl fmt::Debug for Expr {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(formatter, "{:?}", self.expression)
    }
}


/// Execute options
pub struct ExecOptions<'a> {
    expr: &'a Expr,
    contexts: Option<&'a [Context]>,
    functions: Option<&'a Functions>,
}

impl<'a> ExecOptions<'a> {
    /// Create an option.
    #[must_use] pub const fn new(expr: &'a Expr) -> Self {
        ExecOptions {
            expr,
            contexts: None,
            functions: None,
        }
    }

    /// Set contexts.
    pub const fn contexts(&mut self, contexts: &'a [Context]) -> &mut ExecOptions<'a> {
        self.contexts = Some(contexts);
        self
    }

    /// Set functions.
    pub const fn functions(&mut self, functions: &'a Functions) -> &mut ExecOptions<'a> {
        self.functions = Some(functions);
        self
    }

    /// Execute the compiled expression.
    pub fn exec(&self) -> Result<Value, Error> {
        let empty_contexts = create_empty_contexts();
        let empty_functions = Functions::new();

        let contexts = self.contexts.unwrap_or(&empty_contexts);
        let functions = self.functions.unwrap_or(&empty_functions);

        if let Some(compiled) = self.expr.get_compiled() {
            compiled(contexts, functions)
        } else {
            Tree::new(self.expr.expression.clone()).compile()?(contexts, functions)
        }
    }
}


fn create_empty_contexts() -> Contexts {
    vec![Context::new()]
}