Merge branch 'main' into main

This commit is contained in:
ISibboI 2023-05-27 10:42:40 +03:00 committed by GitHub
commit e63a99af80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 181 additions and 25 deletions

View File

@ -16,6 +16,18 @@
### Contributors
## [10.0.0](https://github.com/ISibboI/evalexpr/compare/9.1.0...10.0.0) - 2023-05-21
### Added
* Builtin functions can now be disabled (#129)
### Contributors
My warmhearted thanks goes to:
* [hexofyore](https://github.com/hexofyore)
## [9.1.0](https://github.com/ISibboI/evalexpr/compare/9.0.0...9.1.0) - 2023-05-16
### Added
@ -28,23 +40,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

View File

@ -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"]

View File

@ -334,7 +334,20 @@ For more information about user-defined functions, refer to the respective [sect
### Builtin Functions
This crate offers a set of builtin functions.
This crate offers a set of builtin functions (see below for a full list).
They can be disabled if needed as follows:
```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"))));
```
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 |
|----------------------|-----------------|-------------------------------|-------------|

View File

@ -22,6 +22,13 @@ 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 functions are disabled.
fn are_builtin_functions_disabled(&self) -> bool;
/// 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<()>;
}
/// A context that allows to assign to variables.
@ -66,6 +73,7 @@ pub trait GetFunctionContext: Context {
}*/
/// A context that returns `None` for each identifier.
/// Builtin functions are disabled and cannot be enabled.
#[derive(Debug, Default)]
pub struct EmptyContext;
@ -79,6 +87,20 @@ impl Context for EmptyContext {
identifier.to_string(),
))
}
/// 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<()> {
if disabled {
Ok(())
} else {
Err(EvalexprError::BuiltinFunctionsCannotBeEnabled)
}
}
}
impl<'a> IterateVariablesContext<'a> for EmptyContext {
@ -94,6 +116,50 @@ impl<'a> IterateVariablesContext<'a> for EmptyContext {
}
}
/// A context that returns `None` for each identifier.
/// Builtin functions are enabled and cannot be disabled.
#[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 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<()> {
if disabled {
Err(EvalexprError::BuiltinFunctionsCannotBeDisabled)
} else {
Ok(())
}
}
}
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.*
@ -105,6 +171,9 @@ pub struct HashMapContext {
variables: HashMap<String, Value>,
#[cfg_attr(feature = "serde_support", serde(skip))]
functions: HashMap<String, Function>,
/// True if builtin functions are disabled.
without_builtin_functions: bool,
}
impl HashMapContext {
@ -128,6 +197,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 {

View File

@ -113,6 +113,12 @@ impl fmt::Display for EvalexprError {
regex, message
),
ContextNotMutable => write!(f, "Cannot manipulate 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),
}

View File

@ -203,6 +203,12 @@ pub enum EvalexprError {
/// An escape sequence within a string literal is illegal.
IllegalEscapeSequence(String),
/// 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),
}

View File

@ -317,7 +317,20 @@
//!
//! ### Builtin Functions
//!
//! This crate offers a set of builtin functions.
//! This crate offers a set of builtin functions (see below for a full list).
//! They can be disabled if needed as follows:
//!
//! ```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"))));
//! ```
//!
//! 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 |
//! |----------------------|-----------------|-------------------------------|-------------|
@ -539,7 +552,7 @@ extern crate serde_derive;
pub use crate::{
context::{
Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext,
HashMapContext, IterateVariablesContext,
EmptyContextWithBuiltinFunctions, HashMapContext, IterateVariablesContext,
},
error::{EvalexprError, EvalexprResult},
function::Function,

View File

@ -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 {

View File

@ -1520,12 +1520,39 @@ 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(true), Ok(()));
assert_eq!(
context.set_builtin_functions_disabled(false),
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(false), Ok(()));
assert_eq!(
context.set_builtin_functions_disabled(true),
Err(EvalexprError::BuiltinFunctionsCannotBeDisabled)
);
}
#[test]
@ -2248,3 +2275,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).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(
"max"
)))
);
}