From 9a53ce9893fac48b1734e570c66078e8e79d34e1 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sun, 21 May 2023 09:57:26 +0300 Subject: [PATCH 1/7] Fix changelog. --- CHANGELOG.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b3c587..9f021d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,23 +28,6 @@ My warmhearted thanks goes to: * [nickisyourfan](https://github.com/nickisyourfan) -## [8.2.0](https://github.com/ISibboI/evalexpr/compare/8.1.0...8.2.0) - 2023-04-13 - -### Added - -* `EvalExprError` now derives `Clone` (#116) - -### Changed - -* Occurrences of `f64` and `i64` have been replaced with the type aliases `FloatType` and `IntType` where applicable (#113) - -### Contributors - -My warmhearted thanks goes to: - -* [Natan Freeman](https://github.com/NatanFreeman) -* [Claus Matzinger](https://github.com/celaus) - ## [9.0.0](https://github.com/ISibboI/evalexpr/compare/8.2.0...9.0.0) - 2023-04-13 ### Fixed From 50532c5db5ba388a88646e62bfb6744063851a80 Mon Sep 17 00:00:00 2001 From: Codex Yore Date: Thu, 18 May 2023 23:16:47 +0545 Subject: [PATCH 2/7] feature_method_to_disable_builtin_function --- src/context/mod.rs | 21 +++++++++++++++++++++ src/lib.rs | 11 +++++++++++ src/operator/mod.rs | 4 +++- tests/integration.rs | 11 +++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index e05f893..5cbf88d 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -22,6 +22,12 @@ pub trait Context { /// Calls the function that is linked to the given identifier with the given argument. /// If no function with the given identifier is found, this method returns `EvalexprError::FunctionIdentifierNotFound`. fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult; + + /// Checks if builtin function has been disabled. + fn is_builtin_fn_disabled(&self) -> bool; + + /// Disables Builtin function. + fn disable_builtin_fn(&mut self); } /// A context that allows to assign to variables. @@ -79,6 +85,12 @@ impl Context for EmptyContext { identifier.to_string(), )) } + /// Builtin functions can't be disbaled for Empty Context. + fn disable_builtin_fn(&mut self) {} + /// Builtin functions are always disabled for Empty Context. + fn is_builtin_fn_disabled(&self) -> bool { + true + } } impl<'a> IterateVariablesContext<'a> for EmptyContext { @@ -105,6 +117,7 @@ pub struct HashMapContext { variables: HashMap, #[cfg_attr(feature = "serde_support", serde(skip))] functions: HashMap, + without_builtin_fn: bool, } impl HashMapContext { @@ -128,6 +141,14 @@ impl Context for HashMapContext { )) } } + + fn disable_builtin_fn(&mut self) { + self.without_builtin_fn = true; + } + + fn is_builtin_fn_disabled(&self) -> bool { + self.without_builtin_fn + } } impl ContextWithMutableVariables for HashMapContext { diff --git a/src/lib.rs b/src/lib.rs index cfd5c40..fe3e703 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,6 +252,8 @@ //! When assigning to variables, the assignment is stored in a 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. +//! 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: //! //! ```rust @@ -270,6 +272,12 @@ //! Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); //! // Reading a variable does not require a mutable context //! 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))); +//! //Disabling builtin function in Context. +//! 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")))); //! //! ``` //! @@ -377,6 +385,9 @@ //! Otherwise, a float is returned. //! //! 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 //! diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 218e7ad..8ddda53 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -459,7 +459,9 @@ impl Operator { let arguments = &arguments[0]; match context.call_function(identifier, arguments) { - Err(EvalexprError::FunctionIdentifierNotFound(_)) => { + Err(EvalexprError::FunctionIdentifierNotFound(_)) + if !context.is_builtin_fn_disabled() => + { if let Some(builtin_function) = builtin_function(identifier) { builtin_function.call(arguments) } else { diff --git a/tests/integration.rs b/tests/integration.rs index e6c114e..598d7c2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2243,3 +2243,14 @@ fn test_negative_power() { assert_eq!(eval("(-3)^-2"), Ok(Value::Float(1.0 / 9.0))); assert_eq!(eval("-(3^-2)"), Ok(Value::Float(-1.0 / 9.0))); } + +#[test] +fn test_disabling_builtin_fn() { + let mut context = HashMapContext::new(); + // Built in functions are enabled by default. + assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3))); + // Disabling builtin function in Context. + context.disable_builtin_fn(); + // Builting functions are disabled and using them returns Error. + assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); +} \ No newline at end of file From 24c17491439e564f7b1e341f628839b24bfee4b5 Mon Sep 17 00:00:00 2001 From: Codex Yore Date: Fri, 19 May 2023 21:30:21 +0545 Subject: [PATCH 3/7] Resolving issues for PR #129 --- README.md | 15 +++++++++++ src/context/mod.rs | 60 ++++++++++++++++++++++++++++++++++++-------- src/error/display.rs | 1 + src/error/mod.rs | 3 +++ src/lib.rs | 18 +++++++------ src/operator/mod.rs | 2 +- tests/integration.rs | 39 ++++++++++++++++++++++------ 7 files changed, 113 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f58d1d1..8c79758 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,10 @@ assert_eq!(eval_with_context_mut("a = 5.5", &mut context), Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); // Reading a variable does not require a mutable context 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. 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. 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). ### 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. @@ -395,6 +407,9 @@ Otherwise, a float is returned. 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 Operators take values as arguments and produce values as results. diff --git a/src/context/mod.rs b/src/context/mod.rs index 5cbf88d..cc79f17 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -24,10 +24,10 @@ pub trait Context { fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult; /// Checks if builtin function has been disabled. - fn is_builtin_fn_disabled(&self) -> bool; + fn are_builtin_functions_disabled(&self) -> bool; /// 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. @@ -72,6 +72,7 @@ pub trait GetFunctionContext: Context { }*/ /// A context that returns `None` for each identifier. +/// Builtin functiions are disabled. #[derive(Debug, Default)] pub struct EmptyContext; @@ -85,10 +86,12 @@ impl Context for EmptyContext { identifier.to_string(), )) } - /// Builtin functions can't be disbaled for Empty Context. - fn disable_builtin_fn(&mut self) {} + /// Builtin functions can't be enabled for Empty Context. + fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> { + Err(EvalexprError::InvalidBuiltinFunctionsContext) + } /// Builtin functions are always disabled for Empty Context. - fn is_builtin_fn_disabled(&self) -> bool { + fn are_builtin_functions_disabled(&self) -> bool { 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 { + 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; + + 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. /// /// *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, #[cfg_attr(feature = "serde_support", serde(skip))] functions: HashMap, - without_builtin_fn: bool, + without_builtin_functions: bool, } impl HashMapContext { @@ -142,12 +181,13 @@ impl Context for HashMapContext { } } - fn disable_builtin_fn(&mut self) { - self.without_builtin_fn = true; + fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { + self.without_builtin_functions = disabled; + Ok(()) } - fn is_builtin_fn_disabled(&self) -> bool { - self.without_builtin_fn + fn are_builtin_functions_disabled(&self) -> bool { + self.without_builtin_functions } } diff --git a/src/error/display.rs b/src/error/display.rs index 61e53b9..bb3d418 100644 --- a/src/error/display.rs +++ b/src/error/display.rs @@ -113,6 +113,7 @@ impl fmt::Display for EvalexprError { regex, message ), ContextNotMutable => write!(f, "Cannot manipulate context"), + InvalidBuiltinFunctionsContext => write!(f, "Invalid Builtin Functions Context"), IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string), CustomMessage(message) => write!(f, "Error: {}", message), } diff --git a/src/error/mod.rs b/src/error/mod.rs index 5154d6f..87a9e64 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -203,6 +203,9 @@ pub enum EvalexprError { /// An escape sequence within a string literal is illegal. IllegalEscapeSequence(String), + /// Empty Context can't have builtin functions enabled + InvalidBuiltinFunctionsContext, + /// A custom error explained by its message. CustomMessage(String), } diff --git a/src/lib.rs b/src/lib.rs index fe3e703..2d40af8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,8 +252,6 @@ //! When assigning to variables, the assignment is stored in a 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. -//! 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: //! //! ```rust @@ -274,9 +272,7 @@ //! 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))); -//! //Disabling builtin function in Context. -//! context.disable_builtin_fn(); -//! // Builtin functions are disabled and using them returns Error. +//! context.set_builtin_functions_disabled(true); //! 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. //! 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. //! 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). //! //! ### 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. //! @@ -385,7 +389,7 @@ //! Otherwise, a float is returned. //! //! 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(). //! @@ -549,7 +553,7 @@ extern crate serde_derive; pub use crate::{ context::{ Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext, - HashMapContext, IterateVariablesContext, + EmptyContextWithBuiltinFunctions, HashMapContext, IterateVariablesContext, }, error::{EvalexprError, EvalexprResult}, function::Function, diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 8ddda53..386357a 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -460,7 +460,7 @@ impl Operator { match context.call_function(identifier, arguments) { Err(EvalexprError::FunctionIdentifierNotFound(_)) - if !context.is_builtin_fn_disabled() => + if !context.are_builtin_functions_disabled() => { if let Some(builtin_function) = builtin_function(identifier) { builtin_function.call(arguments) diff --git a/tests/integration.rs b/tests/integration.rs index 598d7c2..b592c50 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1515,12 +1515,32 @@ fn test_type_errors_in_binary_operators() { #[test] fn test_empty_context() { - let context = EmptyContext; + let mut context = EmptyContext; assert_eq!(context.get_value("abc"), None); assert_eq!( context.call_function("abc", &Value::Empty), 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] @@ -2245,12 +2265,17 @@ fn test_negative_power() { } #[test] -fn test_disabling_builtin_fn() { +fn test_builtin_functions_context() { let mut context = HashMapContext::new(); - // Built in functions are enabled by default. - assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3))); + // Builtin functions are enabled by default for HashMapContext. + assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3))); // Disabling builtin function in Context. - context.disable_builtin_fn(); + context.set_builtin_functions_disabled(true); // Builting functions are disabled and using them returns Error. - assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); -} \ No newline at end of file + assert_eq!( + eval_with_context("max(1,3)", &context), + Err(EvalexprError::FunctionIdentifierNotFound(String::from( + "max" + ))) + ); +} From 5a4dcf95163f948e35a899349e2b628885840ee7 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sun, 21 May 2023 10:19:26 +0300 Subject: [PATCH 4/7] Fix docs and error types. --- README.md | 26 ++++++++++++-------------- src/context/mod.rs | 35 ++++++++++++++++++++++------------- src/error/display.rs | 7 ++++++- src/error/mod.rs | 7 +++++-- src/lib.rs | 26 ++++++++++++-------------- tests/integration.rs | 13 +++++++++---- 6 files changed, 66 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 8c79758..9682dc0 100644 --- a/README.md +++ b/README.md @@ -287,10 +287,6 @@ assert_eq!(eval_with_context_mut("a = 5.5", &mut context), Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); // Reading a variable does not require a mutable context 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")))); ``` @@ -304,8 +300,6 @@ This means that assigning to `a` again with a different type yields an error. Type unsafe contexts may be implemented if requested. 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. Take a look at the following example: @@ -339,14 +333,21 @@ 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). ### 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 +This crate offers a set of builtin functions (see below for a full list). +They can be disabled if needed as follows: -It's enabled for EmptyContextWithBuiltinfunctions. It can't be disabled. +```rust +use evalexpr::*; +let mut context = HashMapContext::new(); +assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3))); +context.set_builtin_functions_disabled(true).unwrap(); // Do proper error handling here +assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); +``` -This crate offers a set of builtin functions. +Not all contexts support enabling or disabling builtin functions. +Specifically the `EmptyContext` has builtin functions disabled by default, and they cannot be enabled. +Symmetrically, the `EmptyContextWithBuiltinFunctions` has builtin functions enabled by default, and they cannot be disabled. | Identifier | Argument Amount | Argument Types | Description | |----------------------|-----------------|-------------------------------|-------------| @@ -407,9 +408,6 @@ Otherwise, a float is returned. 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 Operators take values as arguments and produce values as results. diff --git a/src/context/mod.rs b/src/context/mod.rs index cc79f17..63dc379 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -23,10 +23,11 @@ pub trait Context { /// If no function with the given identifier is found, this method returns `EvalexprError::FunctionIdentifierNotFound`. fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult; - /// Checks if builtin function has been disabled. + /// Checks if builtin functions are disabled. fn are_builtin_functions_disabled(&self) -> bool; - /// Disables Builtin function. + /// Disables builtin functions if `disabled` is `true`, and enables them otherwise. + /// If the context does not support enabling or disabling builtin functions, an error is returned. fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()>; } @@ -72,7 +73,7 @@ pub trait GetFunctionContext: Context { }*/ /// A context that returns `None` for each identifier. -/// Builtin functiions are disabled. +/// Builtin functions are disabled and cannot be enabled. #[derive(Debug, Default)] pub struct EmptyContext; @@ -86,14 +87,16 @@ impl Context for EmptyContext { identifier.to_string(), )) } - /// Builtin functions can't be enabled for Empty Context. - 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 `EmptyContext`. fn are_builtin_functions_disabled(&self) -> bool { true } + + /// Builtin functions can't be enabled for `EmptyContext`. + fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> { + Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) + } } impl<'a> IterateVariablesContext<'a> for EmptyContext { @@ -109,7 +112,8 @@ impl<'a> IterateVariablesContext<'a> for EmptyContext { } } -/// Same as Empty Context except Builtin functions are enabled. +/// A context that returns `None` for each identifier. +/// Builtin functions are enabled and cannot be disabled. #[derive(Debug, Default)] pub struct EmptyContextWithBuiltinFunctions; @@ -123,14 +127,16 @@ impl Context for EmptyContextWithBuiltinFunctions { 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 } + + /// Builtin functions can't be disabled for EmptyContextWithBuiltinFunctions. + fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> { + Err(EvalexprError::BuiltinFunctionsCannotBeDisabled) + } } impl<'a> IterateVariablesContext<'a> for EmptyContextWithBuiltinFunctions { @@ -145,6 +151,7 @@ impl<'a> IterateVariablesContext<'a> for EmptyContextWithBuiltinFunctions { iter::empty() } } + /// 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.* @@ -156,6 +163,8 @@ pub struct HashMapContext { variables: HashMap, #[cfg_attr(feature = "serde_support", serde(skip))] functions: HashMap, + + /// True if builtin functions are disabled. without_builtin_functions: bool, } diff --git a/src/error/display.rs b/src/error/display.rs index bb3d418..d7a4e75 100644 --- a/src/error/display.rs +++ b/src/error/display.rs @@ -113,7 +113,12 @@ impl fmt::Display for EvalexprError { regex, message ), ContextNotMutable => write!(f, "Cannot manipulate context"), - InvalidBuiltinFunctionsContext => write!(f, "Invalid Builtin Functions Context"), + BuiltinFunctionsCannotBeEnabled => { + write!(f, "This context does not allow enabling builtin functions") + }, + BuiltinFunctionsCannotBeDisabled => { + write!(f, "This context does not allow disabling builtin functions") + }, IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string), CustomMessage(message) => write!(f, "Error: {}", message), } diff --git a/src/error/mod.rs b/src/error/mod.rs index 87a9e64..e896973 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -203,8 +203,11 @@ pub enum EvalexprError { /// An escape sequence within a string literal is illegal. IllegalEscapeSequence(String), - /// Empty Context can't have builtin functions enabled - InvalidBuiltinFunctionsContext, + /// This context does not allow enabling builtin functions. + BuiltinFunctionsCannotBeEnabled, + + /// This context does not allow disabling builtin functions. + BuiltinFunctionsCannotBeDisabled, /// A custom error explained by its message. CustomMessage(String), diff --git a/src/lib.rs b/src/lib.rs index 2d40af8..673443b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,10 +270,6 @@ //! Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); //! // Reading a variable does not require a mutable context //! 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")))); //! //! ``` //! @@ -287,8 +283,6 @@ //! Type unsafe contexts may be implemented if requested. //! 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. //! Take a look at the following example: //! @@ -322,14 +316,21 @@ //! For more information about user-defined functions, refer to the respective [section](#user-defined-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 +//! This crate offers a set of builtin functions (see below for a full list). +//! They can be disabled if needed as follows: //! -//! It's enabled for EmptyContextWithBuiltinfunctions. It can't be disabled. +//! ```rust +//! use evalexpr::*; +//! let mut context = HashMapContext::new(); +//! assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3))); +//! context.set_builtin_functions_disabled(true).unwrap(); // Do proper error handling here +//! assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); +//! ``` //! -//! This crate offers a set of builtin functions. +//! Not all contexts support enabling or disabling builtin functions. +//! Specifically the `EmptyContext` has builtin functions disabled by default, and they cannot be enabled. +//! Symmetrically, the `EmptyContextWithBuiltinFunctions` has builtin functions enabled by default, and they cannot be disabled. //! //! | Identifier | Argument Amount | Argument Types | Description | //! |----------------------|-----------------|-------------------------------|-------------| @@ -390,9 +391,6 @@ //! //! 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 //! //! Operators take values as arguments and produce values as results. diff --git a/tests/integration.rs b/tests/integration.rs index b592c50..37158ae 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1529,17 +1529,22 @@ fn test_empty_context() { ); assert_eq!( context.set_builtin_functions_disabled(false), - Err(EvalexprError::InvalidBuiltinFunctionsContext) + Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) ) } #[test] fn test_empty_context_with_builtin_functions() { let mut context = EmptyContextWithBuiltinFunctions; + assert_eq!(context.get_value("abc"), None); + assert_eq!( + context.call_function("abc", &Value::Empty), + Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned())) + ); assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::Int(3))); assert_eq!( context.set_builtin_functions_disabled(true), - Err(EvalexprError::InvalidBuiltinFunctionsContext) + Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) ); } @@ -2270,8 +2275,8 @@ fn test_builtin_functions_context() { // Builtin functions are enabled by default for HashMapContext. assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3))); // Disabling builtin function in Context. - context.set_builtin_functions_disabled(true); - // Builting functions are disabled and using them returns Error. + context.set_builtin_functions_disabled(true).unwrap(); + // Builtin functions are disabled and using them returns an error. assert_eq!( eval_with_context("max(1,3)", &context), Err(EvalexprError::FunctionIdentifierNotFound(String::from( From c01a23bcf866138f2eee75b7e4abddf20bf60d17 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sun, 21 May 2023 10:22:46 +0300 Subject: [PATCH 5/7] Return `Ok` when disabling builtin functions in `EmptyContext` and enabling builtin functions in `EmptyContextWithBuiltinFunctions`. These are noops anyways. --- src/context/mod.rs | 16 ++++++++++++---- tests/integration.rs | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 63dc379..041f678 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -94,8 +94,12 @@ impl Context for EmptyContext { } /// Builtin functions can't be enabled for `EmptyContext`. - fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> { - Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) + fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { + if disabled { + Ok(()) + } else { + Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) + } } } @@ -134,8 +138,12 @@ impl Context for EmptyContextWithBuiltinFunctions { } /// Builtin functions can't be disabled for EmptyContextWithBuiltinFunctions. - fn set_builtin_functions_disabled(&mut self, _disabled: bool) -> EvalexprResult<()> { - Err(EvalexprError::BuiltinFunctionsCannotBeDisabled) + fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { + if disabled { + Err(EvalexprError::BuiltinFunctionsCannotBeDisabled) + } else { + Ok(()) + } } } diff --git a/tests/integration.rs b/tests/integration.rs index 37158ae..c837fa2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1527,6 +1527,7 @@ fn test_empty_context() { "max" ))) ); + assert_eq!(context.set_builtin_functions_disabled(true), Ok(())); assert_eq!( context.set_builtin_functions_disabled(false), Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) @@ -1542,9 +1543,10 @@ fn test_empty_context_with_builtin_functions() { Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned())) ); assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::Int(3))); + assert_eq!(context.set_builtin_functions_disabled(false), Ok(())); assert_eq!( context.set_builtin_functions_disabled(true), - Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) + Err(EvalexprError::BuiltinFunctionsCannotBeDisabled) ); } From 59ddd64b397bb8b925d378abf3a6e9c9c52b20a7 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sun, 21 May 2023 10:30:00 +0300 Subject: [PATCH 6/7] Update changelog. --- CHANGELOG.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f021d9..e9236bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,17 +16,29 @@ ### Contributors -## [9.1.0](https://github.com/ISibboI/evalexpr/compare/9.0.0...9.1.0) - 2023-05-16 +## [10.0.0](https://github.com/ISibboI/evalexpr/compare/9.1.0...10.0.0) - 2023-05-21 ### Added -* Builtin functions `contains` and `contains_any` (#127) + * Builtin functions can now be disabled (#129) ### Contributors My warmhearted thanks goes to: -* [nickisyourfan](https://github.com/nickisyourfan) + * [hexofyore](https://github.com/hexofyore) + +## [9.1.0](https://github.com/ISibboI/evalexpr/compare/9.0.0...9.1.0) - 2023-05-16 + +### Added + + * Builtin functions `contains` and `contains_any` (#127) + +### Contributors + +My warmhearted thanks goes to: + + * [nickisyourfan](https://github.com/nickisyourfan) ## [9.0.0](https://github.com/ISibboI/evalexpr/compare/8.2.0...9.0.0) - 2023-04-13 From 2b711abb523ea90112c5ab2b87f58d0f00f2bfb8 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sun, 21 May 2023 10:43:34 +0300 Subject: [PATCH 7/7] Release 10.0.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ca89b04..c497c77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "evalexpr" -version = "9.1.0" +version = "10.0.0" description = "A powerful arithmetic and boolean expression evaluator" keywords = ["expression", "evaluate", "evaluator", "arithmetic", "boolean"] categories = ["parsing", "game-engines"]