use std::{collections::HashMap, iter};
use fastn_type::evalexpr::{
function::Function,
value::{value_type::ValueType, Value},
EvalexprError, EvalexprResult,
};
mod predefined;
pub trait Context {
fn get_value(&self, identifier: &str) -> Option<&Value>;
fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult<Value>;
}
pub trait ContextWithMutableVariables: Context {
fn set_value(&mut self, _identifier: String, _value: Value) -> EvalexprResult<()> {
Err(EvalexprError::ContextNotMutable)
}
}
pub trait ContextWithMutableFunctions: Context {
fn set_function(&mut self, _identifier: String, _function: Function) -> EvalexprResult<()> {
Err(EvalexprError::ContextNotMutable)
}
}
pub trait IterateVariablesContext<'a> {
type VariableIterator: 'a + Iterator<Item = (String, Value)>;
type VariableNameIterator: 'a + Iterator<Item = String>;
fn iter_variables(&'a self) -> Self::VariableIterator;
fn iter_variable_names(&'a self) -> Self::VariableNameIterator;
}
#[derive(Debug, Default)]
pub struct EmptyContext;
impl Context for EmptyContext {
fn get_value(&self, _identifier: &str) -> Option<&Value> {
None
}
fn call_function(&self, identifier: &str, _argument: &Value) -> EvalexprResult<Value> {
Err(EvalexprError::FunctionIdentifierNotFound(
identifier.to_string(),
))
}
}
impl<'a> IterateVariablesContext<'a> for EmptyContext {
type VariableIterator = iter::Empty<(String, Value)>;
type VariableNameIterator = iter::Empty<String>;
fn iter_variables(&self) -> Self::VariableIterator {
iter::empty()
}
fn iter_variable_names(&self) -> Self::VariableNameIterator {
iter::empty()
}
}
#[derive(Clone, Debug, Default)]
pub struct HashMapContext {
variables: HashMap<String, Value>,
functions: HashMap<String, Function>,
}
impl HashMapContext {
pub fn new() -> Self {
Default::default()
}
}
impl Context for HashMapContext {
fn get_value(&self, identifier: &str) -> Option<&Value> {
self.variables.get(identifier)
}
fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult<Value> {
if let Some(function) = self.functions.get(identifier) {
function.call(argument)
} else {
Err(EvalexprError::FunctionIdentifierNotFound(
identifier.to_string(),
))
}
}
}
impl ContextWithMutableVariables for HashMapContext {
fn set_value(&mut self, identifier: String, value: Value) -> EvalexprResult<()> {
if let Some(existing_value) = self.variables.get_mut(&identifier) {
if ValueType::from(&existing_value) == ValueType::from(&value) {
*existing_value = value;
return Ok(());
} else {
return Err(EvalexprError::expected_type(existing_value, value));
}
}
self.variables.insert(identifier, value);
Ok(())
}
}
impl ContextWithMutableFunctions for HashMapContext {
fn set_function(&mut self, identifier: String, function: Function) -> EvalexprResult<()> {
self.functions.insert(identifier, function);
Ok(())
}
}
impl<'a> IterateVariablesContext<'a> for HashMapContext {
type VariableIterator = std::iter::Map<
std::collections::hash_map::Iter<'a, String, Value>,
fn((&String, &Value)) -> (String, Value),
>;
type VariableNameIterator =
std::iter::Cloned<std::collections::hash_map::Keys<'a, String, Value>>;
fn iter_variables(&'a self) -> Self::VariableIterator {
self.variables
.iter()
.map(|(string, value)| (string.clone(), value.clone()))
}
fn iter_variable_names(&'a self) -> Self::VariableNameIterator {
self.variables.keys().cloned()
}
}
#[macro_export]
macro_rules! context_map {
( ($ctx:expr) $k:expr => Function::new($($v:tt)*) ) =>
{ $crate::context_map!(($ctx) $k => Function::new($($v)*),) };
( ($ctx:expr) $k:expr => $v:expr ) =>
{ $crate::context_map!(($ctx) $k => $v,) };
( ($ctx:expr) ) => { Ok(()) };
( ($ctx:expr) $k:expr => Function::new($($v:tt)*) , $($tt:tt)*) => {{
$crate::evalexpr::ContextWithMutableFunctions::set_function($ctx, $k.into(), $crate::evalexpr::Function::new($($v)*))
.and($crate::context_map!(($ctx) $($tt)*))
}};
( ($ctx:expr) $k:expr => $v:expr , $($tt:tt)*) => {{
$crate::evalexpr::ContextWithMutableVariables::set_value($ctx, $k.into(), $v.into())
.and($crate::context_map!(($ctx) $($tt)*))
}};
( $($tt:tt)* ) => {{
let mut context = $crate::evalexpr::HashMapContext::new();
$crate::context_map!((&mut context) $($tt)*)
.map(|_| context)
}};
}