diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 1ec2bb6..ff4ba32 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -148,23 +148,59 @@ pub fn builtin_function(identifier: &str) -> Option { let result_index = if arguments[0].as_boolean()? { 1 } else { 2 }; Ok(arguments.swap_remove(result_index)) })), - "some" => Some(Function::new(move |argument| { - let arguments = argument.as_tuple()?; + "contains" => Some(Function::new(move |argument| { + let arguments = argument.as_fixed_len_tuple(2)?; + if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) { + if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b { + Ok(a.contains(b).into()) + } else { + Err(EvalexprError::type_error( + b.clone(), + vec![ + ValueType::String, + ValueType::Int, + ValueType::Float, + ValueType::Boolean, + ], + )) + } + } else { + Err(EvalexprError::expected_tuple(arguments[0].clone())) + } + })), + "contains_any" => Some(Function::new(move |argument| { + let arguments = argument.as_fixed_len_tuple(2)?; if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) { if let Value::Tuple(b) = b { - for item in b { - if a.contains(item) { - return Ok(Value::Boolean(true)); + let mut contains = false; + for value in b { + //if value is not String, Int, Bool, Float error it out + if let Value::String(_) + | Value::Int(_) + | Value::Float(_) + | Value::Boolean(_) = value + { + if a.contains(value) { + contains = true; + } + } else { + return Err(EvalexprError::type_error( + value.clone(), + vec![ + ValueType::String, + ValueType::Int, + ValueType::Float, + ValueType::Boolean, + ], + )); } } + Ok(contains.into()) } else { - if a.contains(&b) { - return Ok(Value::Boolean(true)); - } + Err(EvalexprError::expected_tuple(b.clone())) } - Ok(Value::Boolean(false)) } else { - Ok(Value::Boolean(false)) + Err(EvalexprError::expected_tuple(arguments[0].clone())) } })), "len" => Some(Function::new(|argument| { diff --git a/src/lib.rs b/src/lib.rs index 14c9598..fe090a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -328,7 +328,8 @@ //! | `round` | 1 | Numeric | Returns the nearest integer to a number. Rounds half-way cases away from 0.0 | //! | `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number | //! | `if` | 3 | Boolean, Any, Any | If the first argument is true, returns the second argument, otherwise, returns the third | -//! | `some` | 2 | Tuple, Any | Returns true if second argument exists in first argument(tuple). If second argument is tuple, checks if any exist in first argument. | +//! | `contains` | 2 | Tuple, Any non Tuple | Returns true if second argument exists in first argument. | +//! | `contains_any` | 2 | Tuple, Tuple of Any Non Tuple | Returns true if one of the values in the tuple of second argument exists in first argument(tuple). | //! | `typeof` | 1 | Any | returns "string", "float", "int", "boolean", "tuple", or "empty" depending on the type of the argument | //! | `math::is_nan` | 1 | Numeric | Returns true if the argument is the floating-point value NaN, false if it is another floating-point value, and throws an error if it is not a number | //! | `math::is_finite` | 1 | Numeric | Returns true if the argument is a finite floating-point number, false otherwise | diff --git a/tests/integration.rs b/tests/integration.rs index 5786f2f..e6c114e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -395,6 +395,93 @@ fn test_builtin_functions() { assert_eq!(eval("max(4.0, 3)"), Ok(Value::Float(4.0))); assert_eq!(eval("len(\"foobar\")"), Ok(Value::Int(6))); assert_eq!(eval("len(\"a\", \"b\")"), Ok(Value::Int(2))); + //Contians + assert_eq!( + eval("contains(1, 2, 3)"), + Err(EvalexprError::expected_fixed_len_tuple( + 2, + Value::Tuple(vec![Value::Int(1), Value::Int(2), Value::Int(3)]) + )) + ); + assert_eq!( + eval("contains((\"foo\", \"bar\"), \"bar\")"), + Ok(Value::Boolean(true)) + ); + assert_eq!( + eval("contains((\"foo\", \"bar\"), \"buzz\")"), + Ok(Value::Boolean(false)), + ); + assert_eq!( + eval("contains(\"foo\", \"bar\")"), + Err(EvalexprError::expected_tuple(Value::String("foo".into()))) + ); + assert_eq!( + eval("contains((\"foo\", \"bar\", 123), 123)"), + Ok(Value::Boolean(true)) + ); + assert_eq!( + eval("contains((\"foo\", \"bar\"), (\"buzz\", \"bazz\"))"), + Err(EvalexprError::type_error( + Value::Tuple(vec![ + Value::String("buzz".into()), + Value::String("bazz".into()) + ]), + vec![ + ValueType::String, + ValueType::Int, + ValueType::Float, + ValueType::Boolean + ] + )) + ); + //Contains Any + assert_eq!( + eval("contains_any(1, 2, 3)"), + Err(EvalexprError::expected_fixed_len_tuple( + 2, + Value::Tuple(vec![Value::Int(1), Value::Int(2), Value::Int(3)]) + )) + ); + assert_eq!( + eval("contains_any((\"foo\", \"bar\"), (\"bar\", \"buzz\"))"), + Ok(Value::Boolean(true)) + ); + assert_eq!( + eval("contains_any((\"foo\", \"bar\"), (\"buzz\", \"bazz\"))"), + Ok(Value::Boolean(false)), + ); + assert_eq!( + eval("contains_any((1,2,3), (3,4,5))"), + Ok(Value::Boolean(true)) + ); + assert_eq!( + eval("contains_any((1,2,3), (4,5,6))"), + Ok(Value::Boolean(false)) + ); + assert_eq!( + eval("contains_any((true, false, true, true), (false, false, false))"), + Ok(Value::Boolean(true)) + ); + assert_eq!( + eval("contains_any(\"foo\", \"bar\")"), + Err(EvalexprError::expected_tuple(Value::String("foo".into()))) + ); + assert_eq!( + eval("contains_any((\"foo\", \"bar\"), \"buzz\")"), + Err(EvalexprError::expected_tuple(Value::String("buzz".into()))) + ); + assert_eq!( + eval("contains_any((\"foo\", \"bar\"), (\"buzz\", (1, 2, 3)))"), + Err(EvalexprError::type_error( + Value::Tuple(vec![Value::Int(1), Value::Int(2), Value::Int(3)]), + vec![ + ValueType::String, + ValueType::Int, + ValueType::Float, + ValueType::Boolean + ] + )) + ); // String assert_eq!( eval("str::to_lowercase(\"FOOBAR\")"),