Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature_method_to_disable_builtin_function #129

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"))));

```

Expand All @@ -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:

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.
Expand Down
61 changes: 61 additions & 0 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value>;

/// Checks if builtin function has been disabled.
fn are_builtin_functions_disabled(&self) -> bool;

/// Disables Builtin function.
fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()>;
}

/// A context that allows to assign to variables.
Expand Down Expand Up @@ -66,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;

Expand All @@ -79,6 +86,14 @@ 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.
fn are_builtin_functions_disabled(&self) -> bool {
true
hexofyore marked this conversation as resolved.
Show resolved Hide resolved
}
hexofyore marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'a> IterateVariablesContext<'a> for EmptyContext {
Expand All @@ -94,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.
///
/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.*
Expand All @@ -105,6 +156,7 @@ pub struct HashMapContext {
variables: HashMap<String, Value>,
#[cfg_attr(feature = "serde_support", serde(skip))]
functions: HashMap<String, Function>,
without_builtin_functions: bool,
}

impl HashMapContext {
Expand All @@ -128,6 +180,15 @@ impl Context for HashMapContext {
))
}
}

fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> {
self.without_builtin_functions = disabled;
Ok(())
}

fn are_builtin_functions_disabled(&self) -> bool {
self.without_builtin_functions
}
}

impl ContextWithMutableVariables for HashMapContext {
Expand Down
1 change: 1 addition & 0 deletions src/error/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down
3 changes: 3 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down
17 changes: 16 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@
//! 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.
hexofyore marked this conversation as resolved.
Show resolved Hide resolved
//! 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"))));
//!
//! ```
//!
Expand All @@ -283,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:
//!
Expand Down Expand Up @@ -316,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.
//!
Expand Down Expand Up @@ -378,6 +390,9 @@
//!
//! 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.
Expand Down Expand Up @@ -538,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,
Expand Down
4 changes: 3 additions & 1 deletion src/operator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ impl Operator {
let arguments = &arguments[0];

match context.call_function(identifier, arguments) {
Err(EvalexprError::FunctionIdentifierNotFound(_)) => {
Err(EvalexprError::FunctionIdentifierNotFound(_))
if !context.are_builtin_functions_disabled() =>
{
if let Some(builtin_function) = builtin_function(identifier) {
builtin_function.call(arguments)
} else {
Expand Down
38 changes: 37 additions & 1 deletion tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -2243,3 +2263,19 @@ 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_builtin_functions_context() {
let mut context = HashMapContext::new();
// 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.
assert_eq!(
eval_with_context("max(1,3)", &context),
Err(EvalexprError::FunctionIdentifierNotFound(String::from(
"max"
)))
);
}