Resolving issues for PR #129

This commit is contained in:
Codex Yore 2023-05-19 21:30:21 +05:45 committed by Sebastian Schmidt
parent 50532c5db5
commit 24c1749143
7 changed files with 113 additions and 25 deletions

View File

@ -287,6 +287,10 @@ assert_eq!(eval_with_context_mut("a = 5.5", &mut context),
Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) }));
// Reading a variable does not require a mutable context // Reading a variable does not require a mutable context
assert_eq!(eval_with_context("a", &context), Ok(Value::from(5))); assert_eq!(eval_with_context("a", &context), Ok(Value::from(5)));
// Builtin functions are enabled by default.
assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3)));
context.set_builtin_functions_disabled(true);
assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max"))));
``` ```
@ -300,6 +304,8 @@ This means that assigning to `a` again with a different type yields an error.
Type unsafe contexts may be implemented if requested. Type unsafe contexts may be implemented if requested.
For reading `a`, it is enough to pass an immutable reference. For reading `a`, it is enough to pass an immutable reference.
EmptyContext have builtin functions disabled and can't be enabled.
EmptyContextWithBuiltinFunctions have builtin functions enabled and can't be disabled.
Contexts can also be manipulated in code. Contexts can also be manipulated in code.
Take a look at the following example: Take a look at the following example:
@ -333,6 +339,12 @@ assert_eq!(eval_int_with_context("f 5", &context), Ok(10));
For more information about user-defined functions, refer to the respective [section](#user-defined-functions). For more information about user-defined functions, refer to the respective [section](#user-defined-functions).
### Builtin Functions ### Builtin Functions
Builtin functions are enabled by default for HashMap Context. It can be disabled by calling Context::
set_builtin_functions_disabled
It's disabled for EmptyContext. It can't be enabled for EmptyContext
It's enabled for EmptyContextWithBuiltinfunctions. It can't be disabled.
This crate offers a set of builtin functions. This crate offers a set of builtin functions.
@ -395,6 +407,9 @@ Otherwise, a float is returned.
The regex functions require the feature flag `regex_support`. The regex functions require the feature flag `regex_support`.
Builtin functions are enabled by Default.
It can be disabled by calling Context::disable_builtin_fn().
### Values ### Values
Operators take values as arguments and produce values as results. Operators take values as arguments and produce values as results.

View File

@ -24,10 +24,10 @@ pub trait Context {
fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult<Value>; fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult<Value>;
/// Checks if builtin function has been disabled. /// Checks if builtin function has been disabled.
fn is_builtin_fn_disabled(&self) -> bool; fn are_builtin_functions_disabled(&self) -> bool;
/// Disables Builtin function. /// Disables Builtin function.
fn disable_builtin_fn(&mut self); fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()>;
} }
/// A context that allows to assign to variables. /// A context that allows to assign to variables.
@ -72,6 +72,7 @@ pub trait GetFunctionContext: Context {
}*/ }*/
/// A context that returns `None` for each identifier. /// A context that returns `None` for each identifier.
/// Builtin functiions are disabled.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct EmptyContext; pub struct EmptyContext;
@ -85,10 +86,12 @@ impl Context for EmptyContext {
identifier.to_string(), identifier.to_string(),
)) ))
} }
/// Builtin functions can't be disbaled for Empty Context. /// Builtin functions can't be enabled for Empty Context.
fn disable_builtin_fn(&mut self) {} fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> {
Err(EvalexprError::InvalidBuiltinFunctionsContext)
}
/// Builtin functions are always disabled for Empty Context. /// Builtin functions are always disabled for Empty Context.
fn is_builtin_fn_disabled(&self) -> bool { fn are_builtin_functions_disabled(&self) -> bool {
true true
} }
} }
@ -106,6 +109,42 @@ impl<'a> IterateVariablesContext<'a> for EmptyContext {
} }
} }
/// Same as Empty Context except Builtin functions are enabled.
#[derive(Debug, Default)]
pub struct EmptyContextWithBuiltinFunctions;
impl Context for EmptyContextWithBuiltinFunctions {
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(),
))
}
/// Builtin functions can't be disbaled for EmptyContextWithBuiltinFunctions.
fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> {
Err(EvalexprError::InvalidBuiltinFunctionsContext)
}
/// Builtin functions are always enabled for EmptyContextWithBuiltinFunctions.
fn are_builtin_functions_disabled(&self) -> bool {
false
}
}
impl<'a> IterateVariablesContext<'a> for EmptyContextWithBuiltinFunctions {
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()
}
}
/// A context that stores its mappings in hash maps. /// A context that stores its mappings in hash maps.
/// ///
/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.* /// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.*
@ -117,7 +156,7 @@ pub struct HashMapContext {
variables: HashMap<String, Value>, variables: HashMap<String, Value>,
#[cfg_attr(feature = "serde_support", serde(skip))] #[cfg_attr(feature = "serde_support", serde(skip))]
functions: HashMap<String, Function>, functions: HashMap<String, Function>,
without_builtin_fn: bool, without_builtin_functions: bool,
} }
impl HashMapContext { impl HashMapContext {
@ -142,12 +181,13 @@ impl Context for HashMapContext {
} }
} }
fn disable_builtin_fn(&mut self) { fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> {
self.without_builtin_fn = true; self.without_builtin_functions = disabled;
Ok(())
} }
fn is_builtin_fn_disabled(&self) -> bool { fn are_builtin_functions_disabled(&self) -> bool {
self.without_builtin_fn self.without_builtin_functions
} }
} }

View File

@ -113,6 +113,7 @@ impl fmt::Display for EvalexprError {
regex, message regex, message
), ),
ContextNotMutable => write!(f, "Cannot manipulate context"), ContextNotMutable => write!(f, "Cannot manipulate context"),
InvalidBuiltinFunctionsContext => write!(f, "Invalid Builtin Functions Context"),
IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string), IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string),
CustomMessage(message) => write!(f, "Error: {}", message), CustomMessage(message) => write!(f, "Error: {}", message),
} }

View File

@ -203,6 +203,9 @@ pub enum EvalexprError {
/// An escape sequence within a string literal is illegal. /// An escape sequence within a string literal is illegal.
IllegalEscapeSequence(String), IllegalEscapeSequence(String),
/// Empty Context can't have builtin functions enabled
InvalidBuiltinFunctionsContext,
/// A custom error explained by its message. /// A custom error explained by its message.
CustomMessage(String), CustomMessage(String),
} }

View File

@ -252,8 +252,6 @@
//! When assigning to variables, the assignment is stored in a context. //! When assigning to variables, the assignment is stored in a context.
//! When the variable is read later on, it is read from the context. //! When the variable is read later on, it is read from the context.
//! Contexts can be preserved between multiple calls to eval by creating them yourself. //! Contexts can be preserved between multiple calls to eval by creating them yourself.
//! By default, Builtin functions are diabled.
//! Builtin functions can be disabled by calling Context::disable_builtin_fn().
//! Here is a simple example to show the difference between preserving and not preserving context between evaluations: //! Here is a simple example to show the difference between preserving and not preserving context between evaluations:
//! //!
//! ```rust //! ```rust
@ -274,9 +272,7 @@
//! assert_eq!(eval_with_context("a", &context), Ok(Value::from(5))); //! assert_eq!(eval_with_context("a", &context), Ok(Value::from(5)));
//! // Builtin functions are enabled by default. //! // Builtin functions are enabled by default.
//! assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3))); //! assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3)));
//! //Disabling builtin function in Context. //! context.set_builtin_functions_disabled(true);
//! context.disable_builtin_fn();
//! // Builtin functions are disabled and using them returns Error.
//! assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); //! assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max"))));
//! //!
//! ``` //! ```
@ -291,6 +287,8 @@
//! Type unsafe contexts may be implemented if requested. //! Type unsafe contexts may be implemented if requested.
//! For reading `a`, it is enough to pass an immutable reference. //! For reading `a`, it is enough to pass an immutable reference.
//! //!
//! EmptyContext have builtin functions disabled and can't be enabled.
//! EmptyContextWithBuiltinFunctions have builtin functions enabled and can't be disabled.
//! Contexts can also be manipulated in code. //! Contexts can also be manipulated in code.
//! Take a look at the following example: //! Take a look at the following example:
//! //!
@ -324,6 +322,12 @@
//! For more information about user-defined functions, refer to the respective [section](#user-defined-functions). //! For more information about user-defined functions, refer to the respective [section](#user-defined-functions).
//! //!
//! ### Builtin Functions //! ### Builtin Functions
//! Builtin functions are enabled by default for HashMap Context. It can be disabled by calling Context::
//! set_builtin_functions_disabled
//!
//! It's disabled for EmptyContext. It can't be enabled for EmptyContext
//!
//! It's enabled for EmptyContextWithBuiltinfunctions. It can't be disabled.
//! //!
//! This crate offers a set of builtin functions. //! This crate offers a set of builtin functions.
//! //!
@ -549,7 +553,7 @@ extern crate serde_derive;
pub use crate::{ pub use crate::{
context::{ context::{
Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext, Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext,
HashMapContext, IterateVariablesContext, EmptyContextWithBuiltinFunctions, HashMapContext, IterateVariablesContext,
}, },
error::{EvalexprError, EvalexprResult}, error::{EvalexprError, EvalexprResult},
function::Function, function::Function,

View File

@ -460,7 +460,7 @@ impl Operator {
match context.call_function(identifier, arguments) { match context.call_function(identifier, arguments) {
Err(EvalexprError::FunctionIdentifierNotFound(_)) Err(EvalexprError::FunctionIdentifierNotFound(_))
if !context.is_builtin_fn_disabled() => if !context.are_builtin_functions_disabled() =>
{ {
if let Some(builtin_function) = builtin_function(identifier) { if let Some(builtin_function) = builtin_function(identifier) {
builtin_function.call(arguments) builtin_function.call(arguments)

View File

@ -1515,12 +1515,32 @@ fn test_type_errors_in_binary_operators() {
#[test] #[test]
fn test_empty_context() { fn test_empty_context() {
let context = EmptyContext; let mut context = EmptyContext;
assert_eq!(context.get_value("abc"), None); assert_eq!(context.get_value("abc"), None);
assert_eq!( assert_eq!(
context.call_function("abc", &Value::Empty), context.call_function("abc", &Value::Empty),
Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned())) Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned()))
); );
assert_eq!(
eval_with_context("max(1,3)", &context),
Err(EvalexprError::FunctionIdentifierNotFound(String::from(
"max"
)))
);
assert_eq!(
context.set_builtin_functions_disabled(false),
Err(EvalexprError::InvalidBuiltinFunctionsContext)
)
}
#[test]
fn test_empty_context_with_builtin_functions() {
let mut context = EmptyContextWithBuiltinFunctions;
assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::Int(3)));
assert_eq!(
context.set_builtin_functions_disabled(true),
Err(EvalexprError::InvalidBuiltinFunctionsContext)
);
} }
#[test] #[test]
@ -2245,12 +2265,17 @@ fn test_negative_power() {
} }
#[test] #[test]
fn test_disabling_builtin_fn() { fn test_builtin_functions_context() {
let mut context = HashMapContext::new(); let mut context = HashMapContext::new();
// Built in functions are enabled by default. // Builtin functions are enabled by default for HashMapContext.
assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3))); assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3)));
// Disabling builtin function in Context. // Disabling builtin function in Context.
context.disable_builtin_fn(); context.set_builtin_functions_disabled(true);
// Builting functions are disabled and using them returns Error. // Builting functions are disabled and using them returns Error.
assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); assert_eq!(
eval_with_context("max(1,3)", &context),
Err(EvalexprError::FunctionIdentifierNotFound(String::from(
"max"
)))
);
} }