From bee98999db07b0a79fd84efc9f52efde7b7bc50c Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Fri, 28 May 2021 14:12:26 +0300 Subject: [PATCH 1/2] Make Function clone. To achieve this, the function type wrapped by Function was changed to remove the `Box`. Relates to #73 --- src/context/mod.rs | 2 +- src/function/builtin.rs | 32 ++++++++++++++++---------------- src/function/mod.rs | 9 +++++---- src/lib.rs | 10 +++++----- tests/integration.rs | 25 +++++++++++-------------- 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 8cff836..ef27b6f 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -129,7 +129,7 @@ impl ContextWithMutableFunctions for HashMapContext { /// /// let ctx = evalexpr::context_map! { /// "x" => 8, -/// "f" => Function::new(Box::new(|_| Ok(42.into()) )) +/// "f" => Function::new(|_| Ok(42.into())) /// }.unwrap(); // Do proper error handling here /// /// assert_eq!(eval_with_context("x + f()", &ctx), Ok(50.into())); diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 21f54fc..fa776d4 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -8,7 +8,7 @@ use crate::{ pub fn builtin_function(identifier: &str) -> Option { match identifier { - "min" => Some(Function::new(Box::new(|argument| { + "min" => Some(Function::new(|argument| { let arguments = argument.as_tuple()?; let mut min_int = IntType::max_value(); let mut min_float = 1.0f64 / 0.0f64; @@ -29,8 +29,8 @@ pub fn builtin_function(identifier: &str) -> Option { } else { Ok(Value::Float(min_float)) } - }))), - "max" => Some(Function::new(Box::new(|argument| { + })), + "max" => Some(Function::new(|argument| { let arguments = argument.as_tuple()?; let mut max_int = IntType::min_value(); let mut max_float = -1.0f64 / 0.0f64; @@ -51,9 +51,9 @@ pub fn builtin_function(identifier: &str) -> Option { } else { Ok(Value::Float(max_float)) } - }))), + })), - "len" => Some(Function::new(Box::new(|argument| { + "len" => Some(Function::new(|argument| { if let Ok(subject) = argument.as_string() { Ok(Value::from(subject.len() as i64)) } else if let Ok(subject) = argument.as_tuple() { @@ -64,11 +64,11 @@ pub fn builtin_function(identifier: &str) -> Option { vec![ValueType::String, ValueType::Tuple], )) } - }))), + })), // string functions #[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 subject = arguments[0].as_string()?; @@ -80,9 +80,9 @@ pub fn builtin_function(identifier: &str) -> Option { format!("{}", err), )), } - }))), + })), #[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 subject = arguments[0].as_string()?; @@ -97,19 +97,19 @@ pub fn builtin_function(identifier: &str) -> Option { format!("{}", err), )), } - }))), - "str::to_lowercase" => Some(Function::new(Box::new(|argument| { + })), + "str::to_lowercase" => Some(Function::new(|argument| { let subject = argument.as_string()?; 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()?; 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()?; Ok(Value::from(subject.trim())) - }))), + })), _ => None, } } diff --git a/src/function/mod.rs b/src/function/mod.rs index 6a2b501..029b00e 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -13,20 +13,21 @@ pub(crate) mod builtin; /// use evalexpr::*; /// /// 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()) -/// }))).unwrap(); // Do proper error handling here +/// })).unwrap(); // Do proper error handling here /// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4))); /// ``` +#[derive(Clone)] pub struct Function { - function: Box EvalexprResult>, + function: fn(&Value) -> EvalexprResult, } impl Function { /// Creates a user-defined function. /// /// The `function` is a boxed function that takes a `Value` and returns a `EvalexprResult`. - pub fn new(function: Box EvalexprResult>) -> Self { + pub fn new(function: fn(&Value) -> EvalexprResult) -> Self { Self { function } } diff --git a/src/lib.rs b/src/lib.rs index b9c48a3..934f433 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! let context = context_map! { //! "five" => 5, //! "twelve" => 12, -//! "f" => Function::new(Box::new(|argument| { +//! "f" => Function::new(|argument| { //! if let Ok(int) = argument.as_int() { //! Ok(Value::Int(int / 2)) //! } else if let Ok(float) = argument.as_float() { @@ -64,8 +64,8 @@ //! } else { //! Err(EvalexprError::expected_number(argument.clone())) //! } -//! })), -//! "avg" => Function::new(Box::new(|argument| { +//! }), +//! "avg" => Function::new(|argument| { //! let arguments = argument.as_tuple()?; //! //! if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) { @@ -73,7 +73,7 @@ //! } else { //! Ok(Value::Float((arguments[0].as_number()? + arguments[1].as_number()?) / 2.0)) //! } -//! })) +//! }) //! }.unwrap(); // Do proper error handling here //! //! assert_eq!(eval_with_context("five + 8 > f(twelve)", &context), Ok(Value::from(true))); @@ -302,7 +302,7 @@ //! use evalexpr::*; //! //! 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)); //! assert_eq!(eval_int_with_context("f 5", &context), Ok(10)); //! ``` diff --git a/tests/integration.rs b/tests/integration.rs index 6c3dddc..1bdede7 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -137,7 +137,7 @@ fn test_functions() { context .set_function( "sub2".to_string(), - Function::new(Box::new(|argument| { + Function::new(|argument| { if let Value::Int(int) = argument { Ok(Value::Int(int - 2)) } else if let Value::Float(float) = argument { @@ -145,7 +145,7 @@ fn test_functions() { } else { Err(EvalexprError::expected_number(argument.clone())) } - })), + }), ) .unwrap(); context @@ -168,7 +168,7 @@ fn test_n_ary_functions() { context .set_function( "sub2".into(), - Function::new(Box::new(|argument| { + Function::new(|argument| { if let Value::Int(int) = argument { Ok(Value::Int(int - 2)) } else if let Value::Float(float) = argument { @@ -176,13 +176,13 @@ fn test_n_ary_functions() { } else { Err(EvalexprError::expected_number(argument.clone())) } - })), + }), ) .unwrap(); context .set_function( "avg".into(), - Function::new(Box::new(|argument| { + Function::new(|argument| { let arguments = argument.as_tuple()?; arguments[0].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, )) } - })), + }), ) .unwrap(); context .set_function( "muladd".into(), - Function::new(Box::new(|argument| { + Function::new(|argument| { let arguments = argument.as_tuple()?; arguments[0].as_number()?; arguments[1].as_number()?; @@ -216,27 +216,24 @@ fn test_n_ary_functions() { + arguments[2].as_float()?, )) } - })), + }), ) .unwrap(); context .set_function( "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::Empty => Ok(Value::from(0)), _ => Ok(Value::from(1)), - })), + }), ) .unwrap(); context .set_value("five".to_string(), Value::Int(5)) .unwrap(); context - .set_function( - "function_four".into(), - Function::new(Box::new(|_| Ok(Value::Int(4)))), - ) + .set_function("function_four".into(), Function::new(|_| Ok(Value::Int(4)))) .unwrap(); assert_eq!(eval_with_context("avg(7, 5)", &context), Ok(Value::Int(6))); From 77e148fcd410db6df6fed2078986a91fdb81c59c Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Fri, 28 May 2021 14:27:41 +0300 Subject: [PATCH 2/2] Ensure that Function implements Send and Sync. Relates to #76 --- src/function/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/function/mod.rs b/src/function/mod.rs index 029b00e..21f27e5 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -41,3 +41,9 @@ impl fmt::Debug for Function { write!(f, "Function {{ [...] }}") } } + +/// A trait to ensure a type is `Send` and `Sync`. +/// If implemented for a type, the crate will not compile if the type is not `Send` and `Sync`. +trait IsSendAndSync: Send + Sync {} + +impl IsSendAndSync for Function {}