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:
parent
9fc86a934f
commit
bee98999db
@ -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()));
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/lib.rs
10
src/lib.rs
@ -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));
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -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)));
|
||||||
|
Loading…
Reference in New Issue
Block a user