Make Function clone.

To achieve this, the function type wrapped by Function was changed to remove the `Box`.

Relates to #73
This commit is contained in:
Sebastian Schmidt 2021-05-28 14:12:26 +03:00
parent 9fc86a934f
commit bee98999db
5 changed files with 38 additions and 40 deletions

View File

@ -129,7 +129,7 @@ impl ContextWithMutableFunctions for HashMapContext {
/// ///
/// let ctx = evalexpr::context_map! { /// let ctx = evalexpr::context_map! {
/// "x" => 8, /// "x" => 8,
/// "f" => Function::new(Box::new(|_| Ok(42.into()) )) /// "f" => Function::new(|_| Ok(42.into()))
/// }.unwrap(); // Do proper error handling here /// }.unwrap(); // Do proper error handling here
/// ///
/// assert_eq!(eval_with_context("x + f()", &ctx), Ok(50.into())); /// assert_eq!(eval_with_context("x + f()", &ctx), Ok(50.into()));

View File

@ -8,7 +8,7 @@ use crate::{
pub fn builtin_function(identifier: &str) -> Option<Function> { pub fn builtin_function(identifier: &str) -> Option<Function> {
match identifier { match identifier {
"min" => Some(Function::new(Box::new(|argument| { "min" => Some(Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
let mut min_int = IntType::max_value(); let mut min_int = IntType::max_value();
let mut min_float = 1.0f64 / 0.0f64; let mut min_float = 1.0f64 / 0.0f64;
@ -29,8 +29,8 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
} else { } else {
Ok(Value::Float(min_float)) Ok(Value::Float(min_float))
} }
}))), })),
"max" => Some(Function::new(Box::new(|argument| { "max" => Some(Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
let mut max_int = IntType::min_value(); let mut max_int = IntType::min_value();
let mut max_float = -1.0f64 / 0.0f64; let mut max_float = -1.0f64 / 0.0f64;
@ -51,9 +51,9 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
} else { } else {
Ok(Value::Float(max_float)) Ok(Value::Float(max_float))
} }
}))), })),
"len" => Some(Function::new(Box::new(|argument| { "len" => Some(Function::new(|argument| {
if let Ok(subject) = argument.as_string() { if let Ok(subject) = argument.as_string() {
Ok(Value::from(subject.len() as i64)) Ok(Value::from(subject.len() as i64))
} else if let Ok(subject) = argument.as_tuple() { } else if let Ok(subject) = argument.as_tuple() {
@ -64,11 +64,11 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
vec![ValueType::String, ValueType::Tuple], vec![ValueType::String, ValueType::Tuple],
)) ))
} }
}))), })),
// string functions // string functions
#[cfg(feature = "regex_support")] #[cfg(feature = "regex_support")]
"str::regex_matches" => Some(Function::new(Box::new(|argument| { "str::regex_matches" => Some(Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
let subject = arguments[0].as_string()?; let subject = arguments[0].as_string()?;
@ -80,9 +80,9 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
format!("{}", err), format!("{}", err),
)), )),
} }
}))), })),
#[cfg(feature = "regex_support")] #[cfg(feature = "regex_support")]
"str::regex_replace" => Some(Function::new(Box::new(|argument| { "str::regex_replace" => Some(Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
let subject = arguments[0].as_string()?; let subject = arguments[0].as_string()?;
@ -97,19 +97,19 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
format!("{}", err), format!("{}", err),
)), )),
} }
}))), })),
"str::to_lowercase" => Some(Function::new(Box::new(|argument| { "str::to_lowercase" => Some(Function::new(|argument| {
let subject = argument.as_string()?; let subject = argument.as_string()?;
Ok(Value::from(subject.to_lowercase())) Ok(Value::from(subject.to_lowercase()))
}))), })),
"str::to_uppercase" => Some(Function::new(Box::new(|argument| { "str::to_uppercase" => Some(Function::new(|argument| {
let subject = argument.as_string()?; let subject = argument.as_string()?;
Ok(Value::from(subject.to_uppercase())) Ok(Value::from(subject.to_uppercase()))
}))), })),
"str::trim" => Some(Function::new(Box::new(|argument| { "str::trim" => Some(Function::new(|argument| {
let subject = argument.as_string()?; let subject = argument.as_string()?;
Ok(Value::from(subject.trim())) Ok(Value::from(subject.trim()))
}))), })),
_ => None, _ => None,
} }
} }

View File

@ -13,20 +13,21 @@ pub(crate) mod builtin;
/// use evalexpr::*; /// use evalexpr::*;
/// ///
/// let mut context = HashMapContext::new(); /// let mut context = HashMapContext::new();
/// context.set_function("id".into(), Function::new(Box::new(|argument| { /// context.set_function("id".into(), Function::new(|argument| {
/// Ok(argument.clone()) /// Ok(argument.clone())
/// }))).unwrap(); // Do proper error handling here /// })).unwrap(); // Do proper error handling here
/// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4))); /// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4)));
/// ``` /// ```
#[derive(Clone)]
pub struct Function { pub struct Function {
function: Box<dyn Fn(&Value) -> EvalexprResult<Value>>, function: fn(&Value) -> EvalexprResult<Value>,
} }
impl Function { impl Function {
/// Creates a user-defined function. /// Creates a user-defined function.
/// ///
/// The `function` is a boxed function that takes a `Value` and returns a `EvalexprResult<Value, Error>`. /// The `function` is a boxed function that takes a `Value` and returns a `EvalexprResult<Value, Error>`.
pub fn new(function: Box<dyn Fn(&Value) -> EvalexprResult<Value>>) -> Self { pub fn new(function: fn(&Value) -> EvalexprResult<Value>) -> Self {
Self { function } Self { function }
} }

View File

@ -56,7 +56,7 @@
//! let context = context_map! { //! let context = context_map! {
//! "five" => 5, //! "five" => 5,
//! "twelve" => 12, //! "twelve" => 12,
//! "f" => Function::new(Box::new(|argument| { //! "f" => Function::new(|argument| {
//! if let Ok(int) = argument.as_int() { //! if let Ok(int) = argument.as_int() {
//! Ok(Value::Int(int / 2)) //! Ok(Value::Int(int / 2))
//! } else if let Ok(float) = argument.as_float() { //! } else if let Ok(float) = argument.as_float() {
@ -64,8 +64,8 @@
//! } else { //! } else {
//! Err(EvalexprError::expected_number(argument.clone())) //! Err(EvalexprError::expected_number(argument.clone()))
//! } //! }
//! })), //! }),
//! "avg" => Function::new(Box::new(|argument| { //! "avg" => Function::new(|argument| {
//! let arguments = argument.as_tuple()?; //! let arguments = argument.as_tuple()?;
//! //!
//! if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) { //! if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
@ -73,7 +73,7 @@
//! } else { //! } else {
//! Ok(Value::Float((arguments[0].as_number()? + arguments[1].as_number()?) / 2.0)) //! Ok(Value::Float((arguments[0].as_number()? + arguments[1].as_number()?) / 2.0))
//! } //! }
//! })) //! })
//! }.unwrap(); // Do proper error handling here //! }.unwrap(); // Do proper error handling here
//! //!
//! assert_eq!(eval_with_context("five + 8 > f(twelve)", &context), Ok(Value::from(true))); //! assert_eq!(eval_with_context("five + 8 > f(twelve)", &context), Ok(Value::from(true)));
@ -302,7 +302,7 @@
//! use evalexpr::*; //! use evalexpr::*;
//! //!
//! let context = context_map!{ //! let context = context_map!{
//! "f" => Function::new(Box::new(|args| Ok(Value::from(args.as_int()? + 5)))), //! "f" => Function::new(|args| Ok(Value::from(args.as_int()? + 5))),
//! }.unwrap_or_else(|error| panic!("Error creating context: {}", error)); //! }.unwrap_or_else(|error| panic!("Error creating context: {}", error));
//! assert_eq!(eval_int_with_context("f 5", &context), Ok(10)); //! assert_eq!(eval_int_with_context("f 5", &context), Ok(10));
//! ``` //! ```

View File

@ -137,7 +137,7 @@ fn test_functions() {
context context
.set_function( .set_function(
"sub2".to_string(), "sub2".to_string(),
Function::new(Box::new(|argument| { Function::new(|argument| {
if let Value::Int(int) = argument { if let Value::Int(int) = argument {
Ok(Value::Int(int - 2)) Ok(Value::Int(int - 2))
} else if let Value::Float(float) = argument { } else if let Value::Float(float) = argument {
@ -145,7 +145,7 @@ fn test_functions() {
} else { } else {
Err(EvalexprError::expected_number(argument.clone())) Err(EvalexprError::expected_number(argument.clone()))
} }
})), }),
) )
.unwrap(); .unwrap();
context context
@ -168,7 +168,7 @@ fn test_n_ary_functions() {
context context
.set_function( .set_function(
"sub2".into(), "sub2".into(),
Function::new(Box::new(|argument| { Function::new(|argument| {
if let Value::Int(int) = argument { if let Value::Int(int) = argument {
Ok(Value::Int(int - 2)) Ok(Value::Int(int - 2))
} else if let Value::Float(float) = argument { } else if let Value::Float(float) = argument {
@ -176,13 +176,13 @@ fn test_n_ary_functions() {
} else { } else {
Err(EvalexprError::expected_number(argument.clone())) Err(EvalexprError::expected_number(argument.clone()))
} }
})), }),
) )
.unwrap(); .unwrap();
context context
.set_function( .set_function(
"avg".into(), "avg".into(),
Function::new(Box::new(|argument| { Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
arguments[0].as_number()?; arguments[0].as_number()?;
arguments[1].as_number()?; arguments[1].as_number()?;
@ -194,13 +194,13 @@ fn test_n_ary_functions() {
(arguments[0].as_float()? + arguments[1].as_float()?) / 2.0, (arguments[0].as_float()? + arguments[1].as_float()?) / 2.0,
)) ))
} }
})), }),
) )
.unwrap(); .unwrap();
context context
.set_function( .set_function(
"muladd".into(), "muladd".into(),
Function::new(Box::new(|argument| { Function::new(|argument| {
let arguments = argument.as_tuple()?; let arguments = argument.as_tuple()?;
arguments[0].as_number()?; arguments[0].as_number()?;
arguments[1].as_number()?; arguments[1].as_number()?;
@ -216,27 +216,24 @@ fn test_n_ary_functions() {
+ arguments[2].as_float()?, + arguments[2].as_float()?,
)) ))
} }
})), }),
) )
.unwrap(); .unwrap();
context context
.set_function( .set_function(
"count".into(), "count".into(),
Function::new(Box::new(|arguments| match arguments { Function::new(|arguments| match arguments {
Value::Tuple(tuple) => Ok(Value::from(tuple.len() as IntType)), Value::Tuple(tuple) => Ok(Value::from(tuple.len() as IntType)),
Value::Empty => Ok(Value::from(0)), Value::Empty => Ok(Value::from(0)),
_ => Ok(Value::from(1)), _ => Ok(Value::from(1)),
})), }),
) )
.unwrap(); .unwrap();
context context
.set_value("five".to_string(), Value::Int(5)) .set_value("five".to_string(), Value::Int(5))
.unwrap(); .unwrap();
context context
.set_function( .set_function("function_four".into(), Function::new(|_| Ok(Value::Int(4))))
"function_four".into(),
Function::new(Box::new(|_| Ok(Value::Int(4)))),
)
.unwrap(); .unwrap();
assert_eq!(eval_with_context("avg(7, 5)", &context), Ok(Value::Int(6))); assert_eq!(eval_with_context("avg(7, 5)", &context), Ok(Value::Int(6)));