evalexpr/context/
mod.rs

1//! A context defines methods to retrieve variable values and call functions for literals in an expression tree.
2//! If mutable, it also allows to assign to variables.
3//!
4//! This crate implements two basic variants, the `EmptyContext`, that returns `None` for each identifier and cannot be manipulated, and the `HashMapContext`, that stores its mappings in hash maps.
5//! The HashMapContext is type-safe and returns an error if the user tries to assign a value of a different type than before to an identifier.
6
7use std::{collections::HashMap, iter, marker::PhantomData};
8
9use crate::{
10    error::EvalexprResultValue,
11    function::Function,
12    value::{
13        numeric_types::{default_numeric_types::DefaultNumericTypes, EvalexprNumericTypes},
14        value_type::ValueType,
15        Value,
16    },
17    EvalexprError, EvalexprResult,
18};
19
20mod predefined;
21
22/// An immutable context.
23pub trait Context {
24    /// The numeric types used for evaluation.
25    type NumericTypes: EvalexprNumericTypes;
26
27    /// Returns the value that is linked to the given identifier.
28    fn get_value(&self, identifier: &str) -> Option<&Value<Self::NumericTypes>>;
29
30    /// Calls the function that is linked to the given identifier with the given argument.
31    /// If no function with the given identifier is found, this method returns `EvalexprError::FunctionIdentifierNotFound`.
32    fn call_function(
33        &self,
34        identifier: &str,
35        argument: &Value<Self::NumericTypes>,
36    ) -> EvalexprResultValue<Self::NumericTypes>;
37
38    /// Checks if builtin functions are disabled.
39    fn are_builtin_functions_disabled(&self) -> bool;
40
41    /// Disables builtin functions if `disabled` is `true`, and enables them otherwise.
42    /// If the context does not support enabling or disabling builtin functions, an error is returned.
43    fn set_builtin_functions_disabled(
44        &mut self,
45        disabled: bool,
46    ) -> EvalexprResult<(), Self::NumericTypes>;
47}
48
49/// A context that allows to assign to variables.
50pub trait ContextWithMutableVariables: Context {
51    /// Sets the variable with the given identifier to the given value.
52    fn set_value(
53        &mut self,
54        _identifier: String,
55        _value: Value<Self::NumericTypes>,
56    ) -> EvalexprResult<(), Self::NumericTypes> {
57        Err(EvalexprError::ContextNotMutable)
58    }
59}
60
61/// A context that allows to assign to function identifiers.
62pub trait ContextWithMutableFunctions: Context {
63    /// Sets the function with the given identifier to the given function.
64    fn set_function(
65        &mut self,
66        _identifier: String,
67        _function: Function<Self::NumericTypes>,
68    ) -> EvalexprResult<(), Self::NumericTypes> {
69        Err(EvalexprError::ContextNotMutable)
70    }
71}
72
73/// A context that allows to iterate over its variable names with their values.
74pub trait IterateVariablesContext: Context {
75    /// The iterator type for iterating over variable name-value pairs.
76    type VariableIterator<'a>: Iterator<Item = (String, Value<Self::NumericTypes>)>
77    where
78        Self: 'a;
79    /// The iterator type for iterating over variable names.
80    type VariableNameIterator<'a>: Iterator<Item = String>
81    where
82        Self: 'a;
83
84    /// Returns an iterator over pairs of variable names and values.
85    fn iter_variables(&self) -> Self::VariableIterator<'_>;
86
87    /// Returns an iterator over variable names.
88    fn iter_variable_names(&self) -> Self::VariableNameIterator<'_>;
89}
90
91/*/// A context that allows to retrieve functions programmatically.
92pub trait GetFunctionContext: Context {
93    /// Returns the function that is linked to the given identifier.
94    ///
95    /// This might not be possible for all functions, as some might be hard-coded.
96    /// In this case, a special error variant should be returned (Not yet implemented).
97    fn get_function(&self, identifier: &str) -> Option<&Function>;
98}*/
99
100/// A context that returns `None` for each identifier.
101/// Builtin functions are disabled and cannot be enabled.
102#[derive(Debug)]
103pub struct EmptyContext<NumericTypes>(PhantomData<NumericTypes>);
104
105impl<NumericTypes: EvalexprNumericTypes> Context for EmptyContext<NumericTypes> {
106    type NumericTypes = NumericTypes;
107
108    fn get_value(&self, _identifier: &str) -> Option<&Value<Self::NumericTypes>> {
109        None
110    }
111
112    fn call_function(
113        &self,
114        identifier: &str,
115        _argument: &Value<Self::NumericTypes>,
116    ) -> EvalexprResultValue<Self::NumericTypes> {
117        Err(EvalexprError::FunctionIdentifierNotFound(
118            identifier.to_string(),
119        ))
120    }
121
122    /// Builtin functions are always disabled for `EmptyContext`.
123    fn are_builtin_functions_disabled(&self) -> bool {
124        true
125    }
126
127    /// Builtin functions can't be enabled for `EmptyContext`.
128    fn set_builtin_functions_disabled(
129        &mut self,
130        disabled: bool,
131    ) -> EvalexprResult<(), Self::NumericTypes> {
132        if disabled {
133            Ok(())
134        } else {
135            Err(EvalexprError::BuiltinFunctionsCannotBeEnabled)
136        }
137    }
138}
139
140impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext for EmptyContext<NumericTypes> {
141    type VariableIterator<'a>
142        = iter::Empty<(String, Value<Self::NumericTypes>)>
143    where
144        Self: 'a;
145    type VariableNameIterator<'a>
146        = iter::Empty<String>
147    where
148        Self: 'a;
149
150    fn iter_variables(&self) -> Self::VariableIterator<'_> {
151        iter::empty()
152    }
153
154    fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> {
155        iter::empty()
156    }
157}
158
159impl<NumericTypes> Default for EmptyContext<NumericTypes> {
160    fn default() -> Self {
161        Self(PhantomData)
162    }
163}
164
165/// A context that returns `None` for each identifier.
166/// Builtin functions are enabled and cannot be disabled.
167#[derive(Debug)]
168pub struct EmptyContextWithBuiltinFunctions<NumericTypes>(PhantomData<NumericTypes>);
169
170impl<NumericTypes: EvalexprNumericTypes> Context
171    for EmptyContextWithBuiltinFunctions<NumericTypes>
172{
173    type NumericTypes = NumericTypes;
174
175    fn get_value(&self, _identifier: &str) -> Option<&Value<Self::NumericTypes>> {
176        None
177    }
178
179    fn call_function(
180        &self,
181        identifier: &str,
182        _argument: &Value<Self::NumericTypes>,
183    ) -> EvalexprResultValue<Self::NumericTypes> {
184        Err(EvalexprError::FunctionIdentifierNotFound(
185            identifier.to_string(),
186        ))
187    }
188
189    /// Builtin functions are always enabled for EmptyContextWithBuiltinFunctions.
190    fn are_builtin_functions_disabled(&self) -> bool {
191        false
192    }
193
194    /// Builtin functions can't be disabled for EmptyContextWithBuiltinFunctions.
195    fn set_builtin_functions_disabled(
196        &mut self,
197        disabled: bool,
198    ) -> EvalexprResult<(), Self::NumericTypes> {
199        if disabled {
200            Err(EvalexprError::BuiltinFunctionsCannotBeDisabled)
201        } else {
202            Ok(())
203        }
204    }
205}
206
207impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext
208    for EmptyContextWithBuiltinFunctions<NumericTypes>
209{
210    type VariableIterator<'a>
211        = iter::Empty<(String, Value<Self::NumericTypes>)>
212    where
213        Self: 'a;
214    type VariableNameIterator<'a>
215        = iter::Empty<String>
216    where
217        Self: 'a;
218
219    fn iter_variables(&self) -> Self::VariableIterator<'_> {
220        iter::empty()
221    }
222
223    fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> {
224        iter::empty()
225    }
226}
227
228impl<NumericTypes> Default for EmptyContextWithBuiltinFunctions<NumericTypes> {
229    fn default() -> Self {
230        Self(PhantomData)
231    }
232}
233
234/// A context that stores its mappings in hash maps.
235///
236/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.*
237///
238/// This context is type-safe, meaning that an identifier that is assigned a value of some type once cannot be assigned a value of another type.
239#[derive(Clone, Debug)]
240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241pub struct HashMapContext<NumericTypes: EvalexprNumericTypes = DefaultNumericTypes> {
242    variables: HashMap<String, Value<NumericTypes>>,
243    #[cfg_attr(feature = "serde", serde(skip))]
244    functions: HashMap<String, Function<NumericTypes>>,
245
246    /// True if builtin functions are disabled.
247    without_builtin_functions: bool,
248}
249
250impl<NumericTypes: EvalexprNumericTypes> HashMapContext<NumericTypes> {
251    /// Constructs a `HashMapContext` with no mappings.
252    pub fn new() -> Self {
253        Default::default()
254    }
255
256    /// Removes all variables from the context.
257    /// This allows to reuse the context without allocating a new HashMap.
258    ///
259    /// # Example
260    ///
261    /// ```rust
262    /// # use evalexpr::*;
263    ///
264    /// let mut context = HashMapContext::<DefaultNumericTypes>::new();
265    /// context.set_value("abc".into(), "def".into()).unwrap();
266    /// assert_eq!(context.get_value("abc"), Some(&("def".into())));
267    /// context.clear_variables();
268    /// assert_eq!(context.get_value("abc"), None);
269    /// ```
270    pub fn clear_variables(&mut self) {
271        self.variables.clear()
272    }
273
274    /// Removes all functions from the context.
275    /// This allows to reuse the context without allocating a new HashMap.
276    pub fn clear_functions(&mut self) {
277        self.functions.clear()
278    }
279
280    /// Removes all variables and functions from the context.
281    /// This allows to reuse the context without allocating a new HashMap.
282    ///
283    /// # Example
284    ///
285    /// ```rust
286    /// # use evalexpr::*;
287    ///
288    /// let mut context = HashMapContext::<DefaultNumericTypes>::new();
289    /// context.set_value("abc".into(), "def".into()).unwrap();
290    /// assert_eq!(context.get_value("abc"), Some(&("def".into())));
291    /// context.clear();
292    /// assert_eq!(context.get_value("abc"), None);
293    /// ```
294    pub fn clear(&mut self) {
295        self.clear_variables();
296        self.clear_functions();
297    }
298}
299
300impl<NumericTypes: EvalexprNumericTypes> Context for HashMapContext<NumericTypes> {
301    type NumericTypes = NumericTypes;
302
303    fn get_value(&self, identifier: &str) -> Option<&Value<Self::NumericTypes>> {
304        self.variables.get(identifier)
305    }
306
307    fn call_function(
308        &self,
309        identifier: &str,
310        argument: &Value<Self::NumericTypes>,
311    ) -> EvalexprResultValue<Self::NumericTypes> {
312        if let Some(function) = self.functions.get(identifier) {
313            function.call(argument)
314        } else {
315            Err(EvalexprError::FunctionIdentifierNotFound(
316                identifier.to_string(),
317            ))
318        }
319    }
320
321    fn are_builtin_functions_disabled(&self) -> bool {
322        self.without_builtin_functions
323    }
324
325    fn set_builtin_functions_disabled(
326        &mut self,
327        disabled: bool,
328    ) -> EvalexprResult<(), NumericTypes> {
329        self.without_builtin_functions = disabled;
330        Ok(())
331    }
332}
333
334impl<NumericTypes: EvalexprNumericTypes> ContextWithMutableVariables
335    for HashMapContext<NumericTypes>
336{
337    fn set_value(
338        &mut self,
339        identifier: String,
340        value: Value<Self::NumericTypes>,
341    ) -> EvalexprResult<(), NumericTypes> {
342        if let Some(existing_value) = self.variables.get_mut(&identifier) {
343            if ValueType::from(&existing_value) == ValueType::from(&value) {
344                *existing_value = value;
345                return Ok(());
346            } else {
347                return Err(EvalexprError::expected_type(existing_value, value));
348            }
349        }
350
351        // Implicit else, because `self.variables` and `identifier` are not unborrowed in else
352        self.variables.insert(identifier, value);
353        Ok(())
354    }
355}
356
357impl<NumericTypes: EvalexprNumericTypes> ContextWithMutableFunctions
358    for HashMapContext<NumericTypes>
359{
360    fn set_function(
361        &mut self,
362        identifier: String,
363        function: Function<NumericTypes>,
364    ) -> EvalexprResult<(), Self::NumericTypes> {
365        self.functions.insert(identifier, function);
366        Ok(())
367    }
368}
369
370impl<NumericTypes: EvalexprNumericTypes> IterateVariablesContext for HashMapContext<NumericTypes> {
371    type VariableIterator<'a>
372        = std::iter::Map<
373        std::collections::hash_map::Iter<'a, String, Value<NumericTypes>>,
374        fn((&String, &Value<NumericTypes>)) -> (String, Value<NumericTypes>),
375    >
376    where
377        Self: 'a;
378    type VariableNameIterator<'a>
379        = std::iter::Cloned<std::collections::hash_map::Keys<'a, String, Value<NumericTypes>>>
380    where
381        Self: 'a;
382
383    fn iter_variables(&self) -> Self::VariableIterator<'_> {
384        self.variables
385            .iter()
386            .map(|(string, value)| (string.clone(), value.clone()))
387    }
388
389    fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> {
390        self.variables.keys().cloned()
391    }
392}
393
394impl<NumericTypes: EvalexprNumericTypes> Default for HashMapContext<NumericTypes> {
395    fn default() -> Self {
396        Self {
397            variables: Default::default(),
398            functions: Default::default(),
399            without_builtin_functions: false,
400        }
401    }
402}
403
404/// This macro provides a convenient syntax for creating a static context.
405///
406/// # Examples
407///
408/// ```rust
409/// use evalexpr::*;
410///
411/// let ctx: HashMapContext<DefaultNumericTypes> = context_map! {
412///     "x" => int 8,
413///     "f" => Function::new(|_| Ok(Value::from_int(42)))
414/// }.unwrap(); // Do proper error handling here
415///
416/// assert_eq!(eval_with_context("x + f()", &ctx), Ok(Value::from_int(50)));
417/// ```
418#[macro_export]
419macro_rules! context_map {
420    // Termination (allow missing comma at the end of the argument list)
421    ( ($ctx:expr) $k:expr => Function::new($($v:tt)*) ) =>
422        { $crate::context_map!(($ctx) $k => Function::new($($v)*),) };
423    ( ($ctx:expr) $k:expr => int $v:expr ) =>
424        { $crate::context_map!(($ctx) $k => int $v,)  };
425    ( ($ctx:expr) $k:expr => float $v:expr ) =>
426        { $crate::context_map!(($ctx) $k => float $v,)  };
427    ( ($ctx:expr) $k:expr => $v:expr ) =>
428        { $crate::context_map!(($ctx) $k => $v,)  };
429    // Termination
430    ( ($ctx:expr) ) => { Ok(()) };
431
432    // The user has to specify a literal 'Function::new' in order to create a function
433    ( ($ctx:expr) $k:expr => Function::new($($v:tt)*) , $($tt:tt)*) => {{
434        $crate::ContextWithMutableFunctions::set_function($ctx, $k.into(), $crate::Function::new($($v)*))
435            .and($crate::context_map!(($ctx) $($tt)*))
436    }};
437    // add an integer value, and chain the eventual error with the ones in the next values
438    ( ($ctx:expr) $k:expr => int $v:expr , $($tt:tt)*) => {{
439        $crate::ContextWithMutableVariables::set_value($ctx, $k.into(), $crate::Value::from_int($v.into()))
440            .and($crate::context_map!(($ctx) $($tt)*))
441    }};
442    // add a float value, and chain the eventual error with the ones in the next values
443    ( ($ctx:expr) $k:expr => float $v:expr , $($tt:tt)*) => {{
444        $crate::ContextWithMutableVariables::set_value($ctx, $k.into(), $crate::Value::from_float($v.into()))
445            .and($crate::context_map!(($ctx) $($tt)*))
446    }};
447    // add a value, and chain the eventual error with the ones in the next values
448    ( ($ctx:expr) $k:expr => $v:expr , $($tt:tt)*) => {{
449        $crate::ContextWithMutableVariables::set_value($ctx, $k.into(), $v.into())
450            .and($crate::context_map!(($ctx) $($tt)*))
451    }};
452
453    // Create a context, then recurse to add the values in it
454    ( $($tt:tt)* ) => {{
455        let mut context = $crate::HashMapContext::new();
456        $crate::context_map!((&mut context) $($tt)*)
457            .map(|_| context)
458    }};
459}