diff --git a/README.md b/README.md index cc7a4b7..56bbc6f 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ This crate offers a set of builtin functions. |------------|-----------------|----------------|-------------| | `min` | >= 1 | Numeric | Returns the minimum of the arguments | | `max` | >= 1 | Numeric | Returns the maximum of the arguments | -| `len` | 1 | String | Returns the character length of a string | +| `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) | | `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument | | `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument | | `str::to_lowercase` | 1 | String | Returns the lower-case version of the string | diff --git a/src/error/mod.rs b/src/error/mod.rs index 7baef0d..359dcec 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -5,10 +5,7 @@ //! The module also contains some helper functions starting with `expect_` that check for a condition and return `Err(_)` if the condition is not fulfilled. //! They are meant as shortcuts to not write the same error checking code everywhere. -use crate::{ - token::PartialToken, - value::{value_type::ValueType, TupleType}, -}; +use crate::{token::PartialToken, value::value_type::ValueType}; use crate::value::Value; @@ -110,7 +107,7 @@ pub enum EvalexprError { /// Only use this if there is no other error that describes the expected and provided types in more detail. TypeError { /// The expected types. - expected: TupleType, + expected: Vec, /// The actual value. actual: Value, }, @@ -205,7 +202,7 @@ impl EvalexprError { } /// Constructs `Error::TypeError{actual, expected}`. - pub fn type_error(actual: Value, expected: TupleType) -> Self { + pub fn type_error(actual: Value, expected: Vec) -> Self { EvalexprError::TypeError { actual, expected } } diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 4d61d6c..a5f76c5 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -3,7 +3,7 @@ use regex::Regex; use crate::{ value::{FloatType, IntType}, - EvalexprError, Function, Value, + EvalexprError, Function, Value, ValueType, }; pub fn builtin_function(identifier: &str) -> Option { @@ -54,8 +54,16 @@ pub fn builtin_function(identifier: &str) -> Option { }))), "len" => Some(Function::new(Box::new(|argument| { - let subject = argument.as_string()?; - Ok(Value::from(subject.len() as i64)) + if let Ok(subject) = argument.as_string() { + Ok(Value::from(subject.len() as i64)) + } else if let Ok(subject) = argument.as_tuple() { + Ok(Value::from(subject.len() as i64)) + } else { + Err(EvalexprError::type_error( + argument.clone(), + vec![ValueType::String, ValueType::Tuple], + )) + } }))), // string functions diff --git a/src/lib.rs b/src/lib.rs index c0c18c2..2335adf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -269,7 +269,7 @@ //! |------------|-----------------|----------------|-------------| //! | `min` | >= 1 | Numeric | Returns the minimum of the arguments | //! | `max` | >= 1 | Numeric | Returns the maximum of the arguments | -//! | `len` | 1 | String | Returns the character length of a string | +//! | `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) | //! | `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument | //! | `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument | //! | `str::to_lowercase` | 1 | String | Returns the lower-case version of the string | diff --git a/src/value/value_type.rs b/src/value/value_type.rs index b6837e7..4ea7395 100644 --- a/src/value/value_type.rs +++ b/src/value/value_type.rs @@ -1,7 +1,7 @@ use crate::Value; /// The type of a `Value`. -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum ValueType { /// The `Value::String` type. String, diff --git a/tests/integration.rs b/tests/integration.rs index 353582e..9a240ca 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -277,6 +277,7 @@ fn test_builtin_functions() { assert_eq!(eval("min(4.0, 3)"), Ok(Value::Int(3))); 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))); assert_eq!( eval("str::to_lowercase(\"FOOBAR\")"), Ok(Value::from("foobar")) diff --git a/tests/regex.rs b/tests/regex.rs index c397825..5a9265a 100644 --- a/tests/regex.rs +++ b/tests/regex.rs @@ -27,4 +27,4 @@ fn test_regex_functions() { eval("str::regex_replace(\"foobar\", \".*?(i+)\", \"b$1\")"), Ok(Value::String("foobar".to_owned())) ); -} \ No newline at end of file +}