diff --git a/Cargo.lock b/Cargo.lock index 6a4771d..0aabb3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2409,11 +2409,13 @@ dependencies = [ name = "taurus" version = "0.1.0" dependencies = [ + "base64", "code0-flow", "env_logger", "futures-lite 2.6.0", "lapin", "log", + "rand", "serde", "serde_json", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 1ed2f40..75cd596 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ tokio = { version = "1.44.1", features = ["rt-multi-thread"] } toml = "0.8.0" log = "0.4.27" futures-lite = "2.6.0" +rand = "0.9.1" +base64 = "0.22.1" env_logger = "0.11.8" [dev-dependencies] diff --git a/src/error/mod.rs b/src/error/mod.rs index 8c5936f..3465554 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -4,7 +4,11 @@ use std::{ }; #[derive(Debug, Default, Clone)] -pub struct RuntimeError {} +pub struct RuntimeError { + name: String, + message: String, + suggestion: Option, +} impl Error for RuntimeError {} @@ -13,3 +17,29 @@ impl Display for RuntimeError { write!(f, "&self.function_name.as_str()") } } + +impl RuntimeError { + pub fn new(name: String, message: String, suggestion: Option) -> Self { + Self { + name, + message, + suggestion, + } + } + + pub fn simple_str(name: &str, message: &str) -> Self { + Self { + name: name.to_string(), + message: message.to_string(), + suggestion: None, + } + } + + pub fn simple(name: &str, message: String) -> Self { + Self { + name: name.to_string(), + message: message, + suggestion: None, + } + } +} diff --git a/src/implementation/boolean.rs b/src/implementation/boolean.rs new file mode 100644 index 0000000..528b34e --- /dev/null +++ b/src/implementation/boolean.rs @@ -0,0 +1,538 @@ +use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use tucana::shared::{value::Kind, Value}; + +pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFn)> { + vec![ + ("std::boolean::as_number", as_number), + ("std::boolean::as_text", as_text), + ("std::boolean::from_number", from_number), + ("std::boolean::from_text", from_text), + ("std::boolean::is_equal", is_equal), + ("std::number::negate", negate), + ] +} + +fn as_number(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::BoolValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a boolean value but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.clone() as i64 as f64)), + }) +} + +fn as_text(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::BoolValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a boolean value but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(value.to_string())), + }) +} + +fn from_number(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::NumberValue(number)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number value but received {:?}", values), + )); + }; + + let is_zero = number == &0.0; + + Ok(Value { + kind: Some(Kind::BoolValue(!is_zero)), + }) +} + +fn from_text(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(text)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a string value but received {:?}", values), + )); + }; + + let bool: bool = match text.to_lowercase().parse() { + Ok(value) => value, + Err(_) => { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )) + } + }; + + Ok(Value { + kind: Some(Kind::BoolValue(bool)), + }) +} + +fn is_equal(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::BoolValue(lhs)), + }, Value { + kind: Some(Kind::BoolValue(rhs)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected two boolean values but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} + +fn negate(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::BoolValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a boolean value but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(!value)), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::context::Context; + use tucana::shared::{value::Kind, Value}; + + // Helper function to create a bool value + fn create_bool_value(b: bool) -> Value { + Value { + kind: Some(Kind::BoolValue(b)), + } + } + + // Helper function to create a number value + fn create_number_value(num: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(num)), + } + } + + // Helper function to create a string value + fn create_string_value(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + + // Helper function to create an invalid value (no kind) + fn create_invalid_value() -> Value { + Value { kind: None } + } + + #[test] + fn test_as_number_success() { + let mut ctx = Context::new(); + + // Test true -> 1.0 + let values = vec![create_bool_value(true)]; + let result = as_number(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), + _ => panic!("Expected NumberValue"), + } + + // Test false -> 0.0 + let values = vec![create_bool_value(false)]; + let result = as_number(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_as_number_error() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters (none) + let values = vec![]; + let result = as_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (too many) + let values = vec![create_bool_value(true), create_bool_value(false)]; + let result = as_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (number) + let values = vec![create_number_value(5.0)]; + let result = as_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (string) + let values = vec![create_string_value("hello")]; + let result = as_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid value + let values = vec![create_invalid_value()]; + let result = as_number(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_as_text_success() { + let mut ctx = Context::new(); + + // Test true -> "true" + let values = vec![create_bool_value(true)]; + let result = as_text(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::StringValue(val)) => assert_eq!(val, "true"), + _ => panic!("Expected StringValue"), + } + + // Test false -> "false" + let values = vec![create_bool_value(false)]; + let result = as_text(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::StringValue(val)) => assert_eq!(val, "false"), + _ => panic!("Expected StringValue"), + } + } + + #[test] + fn test_as_text_error() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters (none) + let values = vec![]; + let result = as_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (too many) + let values = vec![create_bool_value(true), create_bool_value(false)]; + let result = as_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (number) + let values = vec![create_number_value(5.0)]; + let result = as_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (string) + let values = vec![create_string_value("hello")]; + let result = as_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid value + let values = vec![create_invalid_value()]; + let result = as_text(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_from_number_success() { + let mut ctx = Context::new(); + + // Test 0.0 -> false + let values = vec![create_number_value(0.0)]; + let result = from_number(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + + // Test positive number -> true + let values = vec![create_number_value(5.0)]; + let result = from_number(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test negative number -> true + let values = vec![create_number_value(-3.5)]; + let result = from_number(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test -0.0 -> false + let values = vec![create_number_value(-0.0)]; + let result = from_number(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_from_number_error() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters (none) + let values = vec![]; + let result = from_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (too many) + let values = vec![create_number_value(5.0), create_number_value(3.0)]; + let result = from_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (bool) + let values = vec![create_bool_value(true)]; + let result = from_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (string) + let values = vec![create_string_value("hello")]; + let result = from_number(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid value + let values = vec![create_invalid_value()]; + let result = from_number(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_from_text_success() { + let mut ctx = Context::new(); + + // Test "true" -> true + let values = vec![create_string_value("true")]; + let result = from_text(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test "false" -> false + let values = vec![create_string_value("false")]; + let result = from_text(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + + // Test "True" -> true (case insensitive) + let values = vec![create_string_value("True")]; + let result = from_text(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test "FALSE" -> false (case insensitive) + let values = vec![create_string_value("FALSE")]; + let result = from_text(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_from_text_error() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters (none) + let values = vec![]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (too many) + let values = vec![create_string_value("true"), create_string_value("false")]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (bool) + let values = vec![create_bool_value(true)]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (number) + let values = vec![create_number_value(5.0)]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid value + let values = vec![create_invalid_value()]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with unparseable text + let values = vec![create_string_value("hello")]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with numeric string + let values = vec![create_string_value("123")]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with empty string + let values = vec![create_string_value("")]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_equal_success() { + let mut ctx = Context::new(); + + // Test true == true -> true + let values = vec![create_bool_value(true), create_bool_value(true)]; + let result = is_equal(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test false == false -> true + let values = vec![create_bool_value(false), create_bool_value(false)]; + let result = is_equal(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test true == false -> false + let values = vec![create_bool_value(true), create_bool_value(false)]; + let result = is_equal(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + + // Test false == true -> false + let values = vec![create_bool_value(false), create_bool_value(true)]; + let result = is_equal(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_is_equal_error() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters (none) + let values = vec![]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (one) + let values = vec![create_bool_value(true)]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (too many) + let values = vec![ + create_bool_value(true), + create_bool_value(false), + create_bool_value(true), + ]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (first parameter) + let values = vec![create_number_value(5.0), create_bool_value(true)]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (second parameter) + let values = vec![create_bool_value(true), create_string_value("hello")]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid values + let values = vec![create_invalid_value(), create_bool_value(true)]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + + let values = vec![create_bool_value(true), create_invalid_value()]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_negate_success() { + let mut ctx = Context::new(); + + // Test !true -> false + let values = vec![create_bool_value(true)]; + let result = negate(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + + // Test !false -> true + let values = vec![create_bool_value(false)]; + let result = negate(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_negate_error() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters (none) + let values = vec![]; + let result = negate(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong number of parameters (too many) + let values = vec![create_bool_value(true), create_bool_value(false)]; + let result = negate(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (number) + let values = vec![create_number_value(5.0)]; + let result = negate(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value type (string) + let values = vec![create_string_value("hello")]; + let result = negate(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid value + let values = vec![create_invalid_value()]; + let result = negate(&values, &mut ctx); + assert!(result.is_err()); + } +} diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index ccf41ce..ed19860 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,245 +1,15 @@ -use tucana::shared::{Value, value::Kind}; +use crate::registry::HandlerFn; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +pub mod boolean; +pub mod number; +pub mod text; pub fn collect() -> Vec<(&'static str, HandlerFn)> { - vec![ - ("std::number::add", add), - ("std::number::multiply", multiply), - ("std::number::substract", substract), - ("std::number::devide", devide), - ("std::number::modulo", modulo), - ("std::number::abs", abs), - ("std::number::is_positive", is_positive), - ("std::number::is_greater", is_greater), - ("std::number::is_less", is_less), - ("std::number::is_zero", is_zero), - ("std::number::from_text", from_text), - ("std::number::as_text", as_text), - ] -} - -fn add(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::NumberValue(lhs + rhs)), - }) -} - -fn multiply(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::NumberValue(lhs * rhs)), - }) -} - -fn substract(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::NumberValue(lhs - rhs)), - }) -} - -fn devide(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::NumberValue(lhs / rhs)), - }) -} - -fn modulo(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::NumberValue(lhs % rhs)), - }) -} - -fn abs(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::NumberValue(value.abs())), - }) -} - -fn is_positive(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::BoolValue(!value.is_sign_negative())), - }) -} - -fn is_greater(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::BoolValue(lhs > rhs)), - }) -} - -fn is_less(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - .. - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::BoolValue(lhs < rhs)), - }) -} - -fn is_zero(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - Ok(Value { - kind: Some(Kind::BoolValue(value == &0.0)), - }) -} - -fn from_text(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::StringValue(string_value)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; - - let value: f64 = match string_value.parse() { - Ok(result) => result, - Err(_) => return Err(RuntimeError::default()), - }; - - Ok(Value { - kind: Some(Kind::NumberValue(value)), - }) -} + let mut result = vec![]; -fn as_text(values: &[Value], _ctx: &mut Context) -> Result { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - .. - }, - ] = values - else { - return Err(RuntimeError::default()); - }; + result.extend(number::collect_number_functions()); + result.extend(boolean::collect_boolean_functions()); + result.extend(text::collect_text_functions()); - Ok(Value { - kind: Some(Kind::StringValue(value.to_string())), - }) + result } diff --git a/src/implementation/number.rs b/src/implementation/number.rs new file mode 100644 index 0000000..49163be --- /dev/null +++ b/src/implementation/number.rs @@ -0,0 +1,1758 @@ +use std::f64; + +use tucana::shared::{Value, value::Kind}; + +use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; + +pub fn collect_number_functions() -> Vec<(&'static str, HandlerFn)> { + vec![ + ("std::number::add", add), + ("std::number::multiply", multiply), + ("std::number::substract", substract), + ("std::number::divide", divide), + ("std::number::modulo", modulo), + ("std::number::abs", abs), + ("std::number::is_positive", is_positive), + ("std::number::is_greater", is_greater), + ("std::number::is_less", is_less), + ("std::number::is_zero", is_zero), + ("std::number::square", square), + ("std::number::exponential", exponential), + ("std::number::pi", pi), + ("std::number::euler", euler), + ("std::number::infinity", infinity), + ("std::number::round_up", round_up), + ("std::number::round_down", round_down), + ("std::number::round", round), + ("std::number::square_root", square_root), + ("std::number::root", root), + ("std::number::log", log), + ("std::number::ln", ln), + ("std::number::from_text", from_text), + ("std::number::as_text", as_text), + ("std::number::min", min), + ("std::number::max", max), + ("std::number::negate", negate), + ("std::number::random", random), + ("std::number::sin", sin), + ("std::number::cos", cos), + ("std::number::tan", tan), + ("std::number::arcsin", arcsin), + ("std::number::arccos", arccos), + ("std::number::arctan", arctan), + ("std::number::sinh", sinh), + ("std::number::cosh", cosh), + ("std::number::clamp", clamp), + ("std::number::is_equal", is_equal), + ] +} + +fn add(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(lhs + rhs)), + }) +} + +fn multiply(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(lhs * rhs)), + }) +} + +fn substract(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(lhs - rhs)), + }) +} + +fn divide(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + if rhs == &0.0 { + return Err(RuntimeError::simple_str( + "DivisionByZero", + "You cannot divide by zero", + )); + } + + Ok(Value { + kind: Some(Kind::NumberValue(lhs / rhs)), + }) +} + +fn modulo(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + if rhs == &0.0 { + return Err(RuntimeError::simple_str( + "DivisionByZero", + "You cannot divide by zero", + )); + } + + Ok(Value { + kind: Some(Kind::NumberValue(lhs % rhs)), + }) +} + +fn abs(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.abs())), + }) +} + +fn is_positive(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(!value.is_sign_negative())), + }) +} + +fn is_greater(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs > rhs)), + }) +} + +fn is_less(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs < rhs)), + }) +} + +fn is_zero(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(value == &0.0)), + }) +} + +fn square(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.powf(2.0))), + }) +} + +fn exponential(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(base)), + }, + Value { + kind: Some(Kind::NumberValue(exponent)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(base.powf(exponent.clone()))), + }) +} + +fn pi(_values: &[Value], _ctx: &mut Context) -> Result { + Ok(Value { + kind: Some(Kind::NumberValue(f64::consts::PI)), + }) +} + +fn euler(_values: &[Value], _ctx: &mut Context) -> Result { + Ok(Value { + kind: Some(Kind::NumberValue(f64::consts::E)), + }) +} + +fn infinity(_values: &[Value], _ctx: &mut Context) -> Result { + Ok(Value { + kind: Some(Kind::NumberValue(f64::INFINITY)), + }) +} + +fn round_up(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(decimal_places)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + let factor = 10_f64.powi(decimal_places.clone() as i32); + + Ok(Value { + kind: Some(Kind::NumberValue((value * factor).ceil() / factor)), + }) +} + +fn round_down(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(decimal_places)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + let factor = 10_f64.powi(decimal_places.clone() as i32); + + Ok(Value { + kind: Some(Kind::NumberValue((value * factor).floor() / factor)), + }) +} + +fn round(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(decimal_places)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + let factor = 10_f64.powi(decimal_places.clone() as i32); + + Ok(Value { + kind: Some(Kind::NumberValue((value * factor).round() / factor)), + }) +} + +fn square_root(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.sqrt())), + }) +} + +fn root(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(root)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.powf(root.clone()))), + }) +} + +fn log(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(log)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.log(log.clone()))), + }) +} + +fn ln(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.ln())), + }) +} + +fn from_text(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::StringValue(string_value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one string as argument but received {:?}", values), + )); + }; + + let value: f64 = match string_value.parse() { + Ok(result) => result, + Err(_) => { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse string as number: {}", string_value), + )); + } + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value)), + }) +} + +fn as_text(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(value.to_string())), + }) +} + +fn min(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(lhs.min(rhs.clone()))), + }) +} + +fn max(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(lhs.max(rhs.clone()))), + }) +} + +fn negate(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(-value)), + }) +} + +fn random(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(min)), + }, + Value { + kind: Some(Kind::NumberValue(max)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(rand::random_range( + min.clone()..max.clone(), + ))), + }) +} + +fn sin(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.sin())), + }) +} + +fn cos(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.cos())), + }) +} + +fn tan(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.tan())), + }) +} + +fn arcsin(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.asin())), + }) +} + +fn arccos(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.acos())), + }) +} + +fn arctan(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.atan())), + }) +} +fn sinh(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.sinh())), + }) +} + +fn cosh(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.cosh())), + }) +} + +fn clamp(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(min)), + }, + Value { + kind: Some(Kind::NumberValue(max)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected three numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.clamp(min.clone(), max.clone()))), + }) +} + +fn is_equal(values: &[Value], _ctx: &mut Context) -> Result { + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::context::Context; + use tucana::shared::{Value, value::Kind}; + + // Helper function to create a number value + fn create_number_value(num: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(num)), + } + } + + // Helper function to create a string value + fn create_string_value(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + + // Helper function to create a bool value + fn create_bool_value(b: bool) -> Value { + Value { + kind: Some(Kind::BoolValue(b)), + } + } + + // Helper function to create an invalid value (no kind) + fn create_invalid_value() -> Value { + Value { kind: None } + } + + #[test] + fn test_add_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(5.0), create_number_value(3.0)]; + let result = add(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_add_runtime_exception() { + let mut ctx = Context::new(); + + // Test with wrong number of parameters + let values = vec![create_number_value(5.0)]; + let result = add(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong value types + let values = vec![create_string_value("hello"), create_number_value(3.0)]; + let result = add(&values, &mut ctx); + assert!(result.is_err()); + + // Test with invalid values + let values = vec![create_invalid_value(), create_number_value(3.0)]; + let result = add(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_multiply_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(4.0), create_number_value(2.5)]; + let result = multiply(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_multiply_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(true), create_number_value(3.0)]; + let result = multiply(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_substract_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(10.0), create_number_value(4.0)]; + let result = substract(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 6.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_substract_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(5.0)]; + let result = substract(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_divide_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(15.0), create_number_value(3.0)]; + let result = divide(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_divide_by_zero_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(10.0), create_number_value(0.0)]; + let result = divide(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_divide_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("not_a_number"), + create_number_value(2.0), + ]; + let result = divide(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_modulo_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(10.0), create_number_value(3.0)]; + let result = modulo(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_modulo_by_zero_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(10.0), create_number_value(0.0)]; + let result = modulo(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_modulo_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_invalid_value(), create_number_value(3.0)]; + let result = modulo(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_abs_success() { + let mut ctx = Context::new(); + + // Test positive number + let values = vec![create_number_value(5.0)]; + let result = abs(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), + _ => panic!("Expected NumberValue"), + } + + // Test negative number + let values = vec![create_number_value(-7.5)]; + let result = abs(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 7.5), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_abs_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("not_a_number")]; + let result = abs(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_positive_success() { + let mut ctx = Context::new(); + + // Test positive number + let values = vec![create_number_value(5.0)]; + let result = is_positive(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test negative number + let values = vec![create_number_value(-5.0)]; + let result = is_positive(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + + // Test zero + let values = vec![create_number_value(0.0)]; + let result = is_positive(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_is_positive_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(true)]; + let result = is_positive(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_greater_success() { + let mut ctx = Context::new(); + + // Test greater + let values = vec![create_number_value(10.0), create_number_value(5.0)]; + let result = is_greater(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test not greater + let values = vec![create_number_value(3.0), create_number_value(7.0)]; + let result = is_greater(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_is_greater_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![ + create_number_value(5.0), + create_string_value("not_a_number"), + ]; + let result = is_greater(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_less_success() { + let mut ctx = Context::new(); + + // Test less + let values = vec![create_number_value(3.0), create_number_value(7.0)]; + let result = is_less(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test not less + let values = vec![create_number_value(10.0), create_number_value(5.0)]; + let result = is_less(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_is_less_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_invalid_value(), create_number_value(5.0)]; + let result = is_less(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_zero_success() { + let mut ctx = Context::new(); + + // Test zero + let values = vec![create_number_value(0.0)]; + let result = is_zero(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test non-zero + let values = vec![create_number_value(5.0)]; + let result = is_zero(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_is_zero_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("zero")]; + let result = is_zero(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_square_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(4.0)]; + let result = square(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 16.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_square_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(false)]; + let result = square(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_exponential_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(2.0), create_number_value(3.0)]; + let result = exponential(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_exponential_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(2.0)]; + let result = exponential(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_pi_success() { + let mut ctx = Context::new(); + let values = vec![]; + let result = pi(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => { + assert!((val - std::f64::consts::PI).abs() < f64::EPSILON) + } + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_euler_success() { + let mut ctx = Context::new(); + let values = vec![]; + let result = euler(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => { + assert!((val - std::f64::consts::E).abs() < f64::EPSILON) + } + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_infinity_success() { + let mut ctx = Context::new(); + let values = vec![]; + let result = infinity(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!(val.is_infinite() && val.is_sign_positive()), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_round_up_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.14159), create_number_value(2.0)]; + let result = round_up(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 3.15), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_round_up_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("3.14"), create_number_value(2.0)]; + let result = round_up(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_round_down_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.14159), create_number_value(2.0)]; + let result = round_down(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 3.14), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_round_down_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.14), create_invalid_value()]; + let result = round_down(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_round_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.14159), create_number_value(2.0)]; + let result = round(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 3.14), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_round_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(true), create_number_value(2.0)]; + let result = round(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_square_root_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(16.0)]; + let result = square_root(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 4.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_square_root_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("sixteen")]; + let result = square_root(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_root_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(8.0), create_number_value(1.0 / 3.0)]; + let result = root(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 2.0).abs() < 0.001), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_root_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(8.0)]; + let result = root(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_log_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(100.0), create_number_value(10.0)]; + let result = log(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 2.0).abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_log_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_invalid_value(), create_number_value(10.0)]; + let result = log(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_ln_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(std::f64::consts::E)]; + let result = ln(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_ln_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(true)]; + let result = ln(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_from_text_success() { + let mut ctx = Context::new(); + let values = vec![create_string_value("42.5")]; + let result = from_text(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 42.5), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_from_text_runtime_exception() { + let mut ctx = Context::new(); + + // Test with invalid string + let values = vec![create_string_value("not_a_number")]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + + // Test with wrong type + let values = vec![create_number_value(42.0)]; + let result = from_text(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_as_text_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(42.5)]; + let result = as_text(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::StringValue(val)) => assert_eq!(val, "42.5"), + _ => panic!("Expected StringValue"), + } + } + + #[test] + fn test_as_text_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("already_text")]; + let result = as_text(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_min_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.0), create_number_value(7.0)]; + let result = min(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_min_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.0), create_bool_value(false)]; + let result = min(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_max_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(3.0), create_number_value(7.0)]; + let result = max(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 7.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_max_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("three"), create_number_value(7.0)]; + let result = max(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_negate_success() { + let mut ctx = Context::new(); + + // Test positive number + let values = vec![create_number_value(5.0)]; + let result = negate(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, -5.0), + _ => panic!("Expected NumberValue"), + } + + // Test negative number + let values = vec![create_number_value(-3.0)]; + let result = negate(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_negate_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_invalid_value()]; + let result = negate(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_random_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(1.0), create_number_value(10.0)]; + let result = random(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => { + assert!(val >= 1.0 && val < 10.0); + } + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_random_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(1.0), create_string_value("ten")]; + let result = random(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_sin_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(std::f64::consts::PI / 2.0)]; + let result = sin(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_sin_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(true)]; + let result = sin(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_cos_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(0.0)]; + let result = cos(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_cos_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("zero")]; + let result = cos(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_tan_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(std::f64::consts::PI / 4.0)]; + let result = tan(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < 0.0001), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_tan_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_invalid_value()]; + let result = tan(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_arcsin_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(1.0)]; + let result = arcsin(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => { + assert!((val - std::f64::consts::PI / 2.0).abs() < f64::EPSILON) + } + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_arcsin_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(false)]; + let result = arcsin(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_arccos_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(1.0)]; + let result = arccos(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!(val.abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_arccos_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("one")]; + let result = arccos(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_arctan_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(1.0)]; + let result = arctan(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => { + assert!((val - std::f64::consts::PI / 4.0).abs() < f64::EPSILON) + } + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_arctan_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_invalid_value()]; + let result = arctan(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_sinh_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(0.0)]; + let result = sinh(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!(val.abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_sinh_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_bool_value(true)]; + let result = sinh(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_cosh_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(0.0)]; + let result = cosh(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_cosh_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_string_value("zero")]; + let result = cosh(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_clamp_success() { + let mut ctx = Context::new(); + + // Test value within range + let values = vec![ + create_number_value(5.0), + create_number_value(1.0), + create_number_value(10.0), + ]; + let result = clamp(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), + _ => panic!("Expected NumberValue"), + } + + // Test value below range + let values = vec![ + create_number_value(-5.0), + create_number_value(1.0), + create_number_value(10.0), + ]; + let result = clamp(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), + _ => panic!("Expected NumberValue"), + } + + // Test value above range + let values = vec![ + create_number_value(15.0), + create_number_value(1.0), + create_number_value(10.0), + ]; + let result = clamp(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_clamp_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(5.0), create_string_value("one")]; + let result = clamp(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_equal_success() { + let mut ctx = Context::new(); + + // Test equal numbers + let values = vec![create_number_value(5.0), create_number_value(5.0)]; + let result = is_equal(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, true), + _ => panic!("Expected BoolValue"), + } + + // Test unequal numbers + let values = vec![create_number_value(5.0), create_number_value(3.0)]; + let result = is_equal(&values, &mut ctx).unwrap(); + match result.kind { + Some(Kind::BoolValue(val)) => assert_eq!(val, false), + _ => panic!("Expected BoolValue"), + } + } + + #[test] + fn test_is_equal_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(5.0), create_bool_value(true)]; + let result = is_equal(&values, &mut ctx); + assert!(result.is_err()); + } +} diff --git a/src/implementation/text.rs b/src/implementation/text.rs new file mode 100644 index 0000000..4d2a724 --- /dev/null +++ b/src/implementation/text.rs @@ -0,0 +1,1783 @@ +use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use base64::Engine; +use tucana::shared::{value::Kind, ListValue, Value}; + +pub fn collect_text_functions() -> Vec<(&'static str, HandlerFn)> { + vec![ + ("std::text::as_bytes", as_bytes), + ("std::text::byte_size", byte_size), + ("std::text::capitalize", capitalize), + ("std::text::lowercase", lowercase), + ("std::text::uppercase", uppercase), + ("std::text::swapcase", swapcase), + ("std::text::trim", trim), + ("std::text::chars", chars), + ("std::text::at", at), + ("std::text::append", append), + ("std::text::prepend", prepend), + ("std::text::insert", insert), + ("std::text::length", length), + ("std::text::reverse", reverse), + ("std::text::remove", remove), + ("std::text::replace", replace), + ("std::text::replace_first", replace_first), + ("std::text::replace_last", replace_last), + ("std::text::hex", hex), + ("std::text::octal", octal), + ("std::text::index_of", index_of), + ("std::text::contains", contains), + ("std::text::split", split), + ("std::text::starts_with", starts_with), + ("std::text::ends_with", ends_with), + ("std::text::to_ascii", to_ascii), + ("std::text::from_ascii", from_ascii), + ("std::text::encode", encode), + ("std::text::decode", decode), + ("std::text::is_equal", is_equal), + ] +} + +fn as_bytes(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one string as argument but received {:?}", values), + )); + }; + + let bytes: Vec = value + .as_bytes() + .iter() + .map(|byte| Value { + kind: Some(Kind::NumberValue(*byte as f64)), + }) + .collect(); + + Ok(Value { + kind: Some(Kind::ListValue(ListValue { values: bytes })), + }) +} + +fn byte_size(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.len() as f64)), + }) +} + +fn capitalize(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let capitalized_value = value + .split(" ") + .map(|word| { + if word.is_empty() { + return String::from(word); + } + + if word.len() == 1 { + return String::from(word.to_uppercase()); + } + + let first = word.chars().nth(0); + + if first.is_some() { + let first = first.unwrap(); + String::from(first).to_uppercase() + &word[1..] + } else { + String::from(word) + } + }) + .collect::>() + .join(" "); + + Ok(Value { + kind: Some(Kind::StringValue(capitalized_value)), + }) +} + +fn uppercase(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(value.to_uppercase())), + }) +} + +fn lowercase(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(value.to_lowercase())), + }) +} + +fn swapcase(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let swapped = value + .chars() + .map(|c| { + if c.is_uppercase() { + c.to_lowercase().collect::() + } else if c.is_lowercase() { + c.to_uppercase().collect::() + } else { + c.to_string() + } + }) + .collect(); + + Ok(Value { + kind: Some(Kind::StringValue(swapped)), + }) +} + +fn chars(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let chars = value + .chars() + .map(|c| Value { + kind: Some(Kind::StringValue(c.to_string())), + }) + .collect::>(); + + Ok(Value { + kind: Some(Kind::ListValue(ListValue { values: chars })), + }) +} + +fn at(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::NumberValue(index)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string followed by one number as arguments but received {:?}", + values + ), + )); + }; + + if index < &0.0 { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected a positive number as the second argument but received {}", + index + ), + )); + } + + let usize_index = index.clone() as usize; + let char = value.chars().into_iter().nth(usize_index); + + match char { + Some(c) => Ok(Value { + kind: Some(Kind::StringValue(c.to_string())), + }), + None => Err(RuntimeError::simple( + "IndexOutOfBoundsRuntimeError", + format!( + "Index {} is out of bounds for string of length {}", + index, + value.len() + ), + )), + } +} + +fn trim(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(value.trim().to_string())), + }) +} + +fn append(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(suffix)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected two numbers as argument but received {:?}", values), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(value.clone() + suffix)), + }) +} + +fn prepend(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(prefix)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::StringValue(prefix.clone() + value)), + }) +} + +fn insert(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::NumberValue(position)), + }, Value { + kind: Some(Kind::StringValue(text)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected string, number, and string as arguments but received {:?}", + values + ), + )); + }; + + let usize_position = position.clone() as usize; + let mut new_value = value.clone(); + new_value.insert_str(usize_position, text.as_str()); + + Ok(Value { + kind: Some(Kind::StringValue(new_value)), + }) +} + +fn length(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::NumberValue(value.chars().count() as f64)), + }) +} + +fn remove(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::NumberValue(from)), + }, Value { + kind: Some(Kind::NumberValue(to)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string followed by two numbers as arguments but received {:?}", + values + ), + )); + }; + + let chars = value.chars().into_iter().collect::>(); + + let new = chars + .into_iter() + .enumerate() + .filter(|&(i, _)| i < from.clone() as usize || i >= to.clone() as usize) + .map(|e| e.1) + .collect::(); + + Ok(Value { + kind: Some(Kind::StringValue(new)), + }) +} + +fn replace(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(old)), + }, Value { + kind: Some(Kind::StringValue(new)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected three strings as arguments but received {:?}", + values + ), + )); + }; + + let replaced = value.replace(old, new); + + Ok(Value { + kind: Some(Kind::StringValue(replaced)), + }) +} + +fn replace_first(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(old)), + }, Value { + kind: Some(Kind::StringValue(new)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected three strings as arguments but received {:?}", + values + ), + )); + }; + + let replaced = value.replacen(old, new, 1); + + Ok(Value { + kind: Some(Kind::StringValue(replaced)), + }) +} + +fn replace_last(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(old)), + }, Value { + kind: Some(Kind::StringValue(new)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected three strings as arguments but received {:?}", + values + ), + )); + }; + + fn replace_last(haystack: &str, needle: &str, replacement: &str) -> String { + if let Some(pos) = haystack.rfind(needle) { + let mut result = + String::with_capacity(haystack.len() - needle.len() + replacement.len()); + result.push_str(&haystack[..pos]); + result.push_str(replacement); + result.push_str(&haystack[pos + needle.len()..]); + result + } else { + haystack.to_string() // kein Vorkommen gefunden, original zurückgeben + } + } + + let replaced = replace_last(value.as_str(), old.as_str(), new.as_str()); + + Ok(Value { + kind: Some(Kind::StringValue(replaced)), + }) +} + +fn hex(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let hex = value + .as_bytes() + .iter() + .map(|byte| format!("{:02x}", byte)) + .collect::(); + + Ok(Value { + kind: Some(Kind::StringValue(hex)), + }) +} + +fn octal(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let hex = value + .as_bytes() + .iter() + .map(|byte| format!("{:03o}", byte)) + .collect::(); + + Ok(Value { + kind: Some(Kind::StringValue(hex)), + }) +} + +fn index_of(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(sub_string)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + let index_option = value.find(sub_string); + + match index_option { + Some(index) => Ok(Value { + kind: Some(Kind::NumberValue(index as f64)), + }), + None => Ok(Value { + kind: Some(Kind::NumberValue(-1.0)), + }), + } +} + +fn contains(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(sub_string)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(value.contains(sub_string))), + }) +} + +fn split(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(delimiter)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + let words = value + .split(delimiter) + .map(|word| Value { + kind: Some(Kind::StringValue(word.to_string())), + }) + .collect::>(); + + Ok(Value { + kind: Some(Kind::ListValue(ListValue { values: words })), + }) +} + +fn reverse(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let reversed = value.chars().rev().collect::(); + + Ok(Value { + kind: Some(Kind::StringValue(reversed)), + }) +} + +fn starts_with(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(prefix)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(value.starts_with(prefix))), + }) +} + +fn ends_with(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(suffix)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(value.ends_with(suffix))), + }) +} + +fn to_ascii(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected one string as an argument but received {:?}", + values + ), + )); + }; + + let ascii_value = value + .bytes() + .map(|b| Value { + kind: Some(Kind::NumberValue(b as f64)), + }) + .collect::>(); + + Ok(Value { + kind: Some(Kind::ListValue(ListValue { + values: ascii_value, + })), + }) +} + +fn from_ascii(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::ListValue(list)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected a list of numbers as an argument but received {:?}", + values + ), + )); + }; + + let string = list + .values + .iter() + .map(|number_value| { + if let Value { + kind: Some(Kind::NumberValue(number)), + } = number_value + { + if number >= &0.0 && number <= &127.0 { + Some(number.clone() as u8 as char) + } else { + None + } + } else { + None + } + }) + .collect::>(); + + match string { + Some(string) => Ok(Value { + kind: Some(Kind::StringValue(string)), + }), + None => Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + "Expected a list of numbers between 0 and 127".to_string(), + )), + } +} + +//TODO: Implement encode function , what about decode? UTF-8, 16 and 32 does not make sense + +fn encode(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(encoding)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + let encoded_string = match encoding.clone().to_lowercase().as_str() { + "base64" => base64::prelude::BASE64_STANDARD.encode(value), + _ => { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Unsupported encoding: {}", encoding), + )) + } + }; + + Ok(Value { + kind: Some(Kind::StringValue(encoded_string)), + }) +} + +fn decode(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(encoding)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + let decoded_string = match encoding.clone().to_lowercase().as_str() { + "base64" => match base64::prelude::BASE64_STANDARD.decode(value) { + Ok(bytes) => match String::from_utf8(bytes) { + Ok(string) => string, + Err(err) => { + return Err(RuntimeError::simple( + "DecodeError", + format!("Failed to decode base64 string: {:?}", err), + )) + } + }, + Err(err) => { + return Err(RuntimeError::simple( + "DecodeError", + format!("Failed to decode base64 string: {:?}", err), + )) + } + }, + _ => { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Unsupported decoding: {}", encoding), + )) + } + }; + + Ok(Value { + kind: Some(Kind::StringValue(decoded_string)), + }) +} + +fn is_equal(values: &[Value], _ctx: &mut Context) -> Result { + let [Value { + kind: Some(Kind::StringValue(lhs)), + }, Value { + kind: Some(Kind::StringValue(rhs)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two strings as arguments but received {:?}", + values + ), + )); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::context::Context; + use tucana::shared::{value::Kind, ListValue, Value}; + + // Helper function to create a string value + fn create_string_value(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + + // Helper function to create a number value + fn create_number_value(num: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(num)), + } + } + + // Helper function to create a bool value + fn create_bool_value(b: bool) -> Value { + Value { + kind: Some(Kind::BoolValue(b)), + } + } + + // Helper function to create a list value + fn create_list_value(values: Vec) -> Value { + Value { + kind: Some(Kind::ListValue(ListValue { values })), + } + } + + #[test] + fn test_as_bytes_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = as_bytes(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 5); + assert_eq!(list.values[0], create_number_value(104.0)); // 'h' + assert_eq!(list.values[1], create_number_value(101.0)); // 'e' + assert_eq!(list.values[2], create_number_value(108.0)); // 'l' + assert_eq!(list.values[3], create_number_value(108.0)); // 'l' + assert_eq!(list.values[4], create_number_value(111.0)); // 'o' + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_as_bytes_empty_string() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = as_bytes(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 0); + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_as_bytes_invalid_argument() { + let mut ctx = Context::new(); + let values = vec![create_number_value(123.0)]; + let result = as_bytes(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_byte_size_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = byte_size(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(5.0)); + } + + #[test] + fn test_byte_size_empty() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = byte_size(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(0.0)); + } + + #[test] + fn test_byte_size_unicode() { + let mut ctx = Context::new(); + let values = vec![create_string_value("café")]; + let result = byte_size(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(5.0)); // 'é' is 2 bytes in UTF-8 + } + + #[test] + fn test_capitalize_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello world")]; + let result = capitalize(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("Hello World")); + } + + #[test] + fn test_capitalize_empty() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = capitalize(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("")); + } + + #[test] + fn test_capitalize_single_char() { + let mut ctx = Context::new(); + let values = vec![create_string_value("a")]; + let result = capitalize(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("A")); + } + + #[test] + fn test_uppercase_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("Hello World")]; + let result = uppercase(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("HELLO WORLD")); + } + + #[test] + fn test_uppercase_already_upper() { + let mut ctx = Context::new(); + let values = vec![create_string_value("HELLO")]; + let result = uppercase(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("HELLO")); + } + + #[test] + fn test_lowercase_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("Hello World")]; + let result = lowercase(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world")); + } + + #[test] + fn test_lowercase_already_lower() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = lowercase(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello")); + } + + #[test] + fn test_swapcase_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("Hello World")]; + let result = swapcase(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hELLO wORLD")); + } + + #[test] + fn test_swapcase_mixed() { + let mut ctx = Context::new(); + let values = vec![create_string_value("HeLLo123")]; + let result = swapcase(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hEllO123")); + } + + #[test] + fn test_chars_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("abc")]; + let result = chars(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 3); + assert_eq!(list.values[0], create_string_value("a")); + assert_eq!(list.values[1], create_string_value("b")); + assert_eq!(list.values[2], create_string_value("c")); + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_chars_empty() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = chars(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 0); + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_at_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_number_value(1.0)]; + let result = at(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("e")); + } + + #[test] + fn test_at_first_char() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_number_value(0.0)]; + let result = at(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("h")); + } + + #[test] + fn test_at_out_of_bounds() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_number_value(10.0)]; + let result = at(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_at_negative_index() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_number_value(-1.0)]; + let result = at(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_trim_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value(" hello world ")]; + let result = trim(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world")); + } + + #[test] + fn test_trim_no_whitespace() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = trim(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello")); + } + + #[test] + fn test_append_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value(" world")]; + let result = append(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world")); + } + + #[test] + fn test_append_empty_suffix() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value("")]; + let result = append(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello")); + } + + #[test] + fn test_prepend_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("world"), create_string_value("hello ")]; + let result = prepend(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world")); + } + + #[test] + fn test_prepend_empty_prefix() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value("")]; + let result = prepend(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello")); + } + + #[test] + fn test_insert_valid() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello"), + create_number_value(2.0), + create_string_value("XXX"), + ]; + let result = insert(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("heXXXllo")); + } + + #[test] + fn test_insert_at_beginning() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello"), + create_number_value(0.0), + create_string_value("XXX"), + ]; + let result = insert(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("XXXhello")); + } + + #[test] + fn test_insert_at_end() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello"), + create_number_value(5.0), + create_string_value("XXX"), + ]; + let result = insert(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("helloXXX")); + } + + #[test] + fn test_length_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = length(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(5.0)); + } + + #[test] + fn test_length_empty() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = length(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(0.0)); + } + + #[test] + fn test_length_unicode() { + let mut ctx = Context::new(); + let values = vec![create_string_value("café")]; + let result = length(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(4.0)); // 4 characters + } + + #[test] + fn test_remove_valid() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_number_value(2.0), + create_number_value(7.0), + ]; + let result = remove(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("heorld")); + } + + #[test] + fn test_remove_from_beginning() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello"), + create_number_value(0.0), + create_number_value(2.0), + ]; + let result = remove(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("llo")); + } + + #[test] + fn test_replace_valid() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world hello"), + create_string_value("hello"), + create_string_value("hi"), + ]; + let result = replace(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hi world hi")); + } + + #[test] + fn test_replace_not_found() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("xyz"), + create_string_value("abc"), + ]; + let result = replace(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world")); + } + + #[test] + fn test_replace_first_valid() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world hello"), + create_string_value("hello"), + create_string_value("hi"), + ]; + let result = replace_first(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hi world hello")); + } + + #[test] + fn test_replace_last_valid() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world hello"), + create_string_value("hello"), + create_string_value("hi"), + ]; + let result = replace_last(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world hi")); + } + + #[test] + fn test_replace_last_not_found() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("xyz"), + create_string_value("abc"), + ]; + let result = replace_last(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello world")); + } + + #[test] + fn test_hex_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = hex(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("68656c6c6f")); + } + + #[test] + fn test_hex_empty() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = hex(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("")); + } + + #[test] + fn test_octal_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("A")]; + let result = octal(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("101")); // 'A' is 65 in ASCII, 101 in octal + } + + #[test] + fn test_index_of_found() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("world"), + ]; + let result = index_of(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(6.0)); + } + + #[test] + fn test_index_of_not_found() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("xyz"), + ]; + let result = index_of(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(-1.0)); + } + + #[test] + fn test_index_of_at_beginning() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("hello"), + ]; + let result = index_of(&values, &mut ctx).unwrap(); + assert_eq!(result, create_number_value(0.0)); + } + + #[test] + fn test_contains_true() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("world"), + ]; + let result = contains(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(true)); + } + + #[test] + fn test_contains_false() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("xyz"), + ]; + let result = contains(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(false)); + } + + #[test] + fn test_split_valid() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello,world,test"), + create_string_value(","), + ]; + let result = split(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 3); + assert_eq!(list.values[0], create_string_value("hello")); + assert_eq!(list.values[1], create_string_value("world")); + assert_eq!(list.values[2], create_string_value("test")); + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_split_no_delimiter() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value(",")]; + let result = split(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 1); + assert_eq!(list.values[0], create_string_value("hello")); + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_reverse_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello")]; + let result = reverse(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("olleh")); + } + + #[test] + fn test_reverse_empty() { + let mut ctx = Context::new(); + let values = vec![create_string_value("")]; + let result = reverse(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("")); + } + + #[test] + fn test_reverse_palindrome() { + let mut ctx = Context::new(); + let values = vec![create_string_value("aba")]; + let result = reverse(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("aba")); + } + + #[test] + fn test_encode_invalid_parameter() { + let mut ctx = Context::new(); + let values = vec![create_string_value("aba")]; + let result = encode(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_encode_invalid_encoding() { + let mut ctx = Context::new(); + let values = vec![create_string_value("aba"), create_string_value("gug")]; + let result = encode(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_encode_correct() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value("BASE64")]; + let result = encode(&values, &mut ctx).unwrap(); + assert_eq!( + result, + Value { + kind: Some(Kind::StringValue(String::from("aGVsbG8="))) + } + ); + } + + #[test] + fn test_decode_invalid_parameter() { + let mut ctx = Context::new(); + let values = vec![create_string_value("aba")]; + let result = decode(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_decode_invalid_encoding() { + let mut ctx = Context::new(); + let values = vec![create_string_value("aba"), create_string_value("gug")]; + let result = decode(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_decode_correct() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("aGVsbG8="), + create_string_value("BASE64"), + ]; + let result = decode(&values, &mut ctx).unwrap(); + assert_eq!( + result, + Value { + kind: Some(Kind::StringValue(String::from("hello"))) + } + ); + } + + #[test] + fn test_starts_with_true() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("hello"), + ]; + let result = starts_with(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(true)); + } + + #[test] + fn test_starts_with_false() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("world"), + ]; + let result = starts_with(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(false)); + } + + #[test] + fn test_ends_with_true() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("world"), + ]; + let result = ends_with(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(true)); + } + + #[test] + fn test_ends_with_false() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("hello world"), + create_string_value("hello"), + ]; + let result = ends_with(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(false)); + } + + #[test] + fn test_to_ascii_valid() { + let mut ctx = Context::new(); + let values = vec![create_string_value("AB")]; + let result = to_ascii(&values, &mut ctx).unwrap(); + + if let Value { + kind: Some(Kind::ListValue(list)), + } = result + { + assert_eq!(list.values.len(), 2); + assert_eq!(list.values[0], create_number_value(65.0)); // 'A' + assert_eq!(list.values[1], create_number_value(66.0)); // 'B' + } else { + panic!("Expected ListValue"); + } + } + + #[test] + fn test_from_ascii_valid() { + let mut ctx = Context::new(); + let ascii_values = vec![ + create_number_value(65.0), // 'A' + create_number_value(66.0), // 'B' + create_number_value(67.0), // 'C' + ]; + let values = vec![create_list_value(ascii_values)]; + let result = from_ascii(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("ABC")); + } + + #[test] + fn test_from_ascii_empty_list() { + let mut ctx = Context::new(); + let values = vec![create_list_value(vec![])]; + let result = from_ascii(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("")); + } + + #[test] + fn test_from_ascii_invalid_range() { + let mut ctx = Context::new(); + let ascii_values = vec![ + create_number_value(65.0), + create_number_value(128.0), // Out of ASCII range + ]; + let values = vec![create_list_value(ascii_values)]; + let result = from_ascii(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_from_ascii_negative_number() { + let mut ctx = Context::new(); + let ascii_values = vec![ + create_number_value(65.0), + create_number_value(-1.0), // Negative + ]; + let values = vec![create_list_value(ascii_values)]; + let result = from_ascii(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_from_ascii_non_number_in_list() { + let mut ctx = Context::new(); + let ascii_values = vec![ + create_number_value(65.0), + create_string_value("invalid"), // Non-number + ]; + let values = vec![create_list_value(ascii_values)]; + let result = from_ascii(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_is_equal_true() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value("hello")]; + let result = is_equal(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(true)); + } + + #[test] + fn test_is_equal_false() { + let mut ctx = Context::new(); + let values = vec![create_string_value("hello"), create_string_value("world")]; + let result = is_equal(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(false)); + } + + #[test] + fn test_is_equal_empty_strings() { + let mut ctx = Context::new(); + let values = vec![create_string_value(""), create_string_value("")]; + let result = is_equal(&values, &mut ctx).unwrap(); + assert_eq!(result, create_bool_value(true)); + } + + // Test invalid arguments for functions requiring specific argument counts + #[test] + fn test_invalid_arguments_single_string() { + let mut ctx = Context::new(); + + // Test functions that expect 1 string argument + let invalid_values = vec![create_number_value(123.0)]; + + assert!(as_bytes(&invalid_values, &mut ctx).is_err()); + assert!(byte_size(&invalid_values, &mut ctx).is_err()); + assert!(capitalize(&invalid_values, &mut ctx).is_err()); + assert!(uppercase(&invalid_values, &mut ctx).is_err()); + assert!(lowercase(&invalid_values, &mut ctx).is_err()); + assert!(swapcase(&invalid_values, &mut ctx).is_err()); + assert!(chars(&invalid_values, &mut ctx).is_err()); + assert!(trim(&invalid_values, &mut ctx).is_err()); + assert!(length(&invalid_values, &mut ctx).is_err()); + assert!(reverse(&invalid_values, &mut ctx).is_err()); + assert!(hex(&invalid_values, &mut ctx).is_err()); + assert!(octal(&invalid_values, &mut ctx).is_err()); + assert!(to_ascii(&invalid_values, &mut ctx).is_err()); + } + + #[test] + fn test_invalid_arguments_two_strings() { + let mut ctx = Context::new(); + + // Test functions that expect 2 string arguments + let invalid_values = vec![create_string_value("hello"), create_number_value(123.0)]; + + assert!(append(&invalid_values, &mut ctx).is_err()); + assert!(prepend(&invalid_values, &mut ctx).is_err()); + assert!(index_of(&invalid_values, &mut ctx).is_err()); + assert!(contains(&invalid_values, &mut ctx).is_err()); + assert!(split(&invalid_values, &mut ctx).is_err()); + assert!(starts_with(&invalid_values, &mut ctx).is_err()); + assert!(ends_with(&invalid_values, &mut ctx).is_err()); + assert!(is_equal(&invalid_values, &mut ctx).is_err()); + } + + #[test] + fn test_invalid_arguments_string_and_number() { + let mut ctx = Context::new(); + + // Test functions that expect string and number + let invalid_values = vec![create_number_value(123.0), create_string_value("test")]; + + assert!(at(&invalid_values, &mut ctx).is_err()); + } + + #[test] + fn test_invalid_arguments_three_params() { + let mut ctx = Context::new(); + + // Test functions that expect 3 arguments + let invalid_values = vec![ + create_string_value("hello"), + create_string_value("test"), + create_number_value(123.0), + ]; + + assert!(insert(&invalid_values, &mut ctx).is_err()); + assert!(remove(&invalid_values, &mut ctx).is_err()); + assert!(replace(&invalid_values, &mut ctx).is_err()); + assert!(replace_first(&invalid_values, &mut ctx).is_err()); + assert!(replace_last(&invalid_values, &mut ctx).is_err()); + } + + #[test] + fn test_wrong_argument_count() { + let mut ctx = Context::new(); + + // Test with wrong number of arguments + let empty_values = vec![]; + let too_many_values = vec![ + create_string_value("test1"), + create_string_value("test2"), + create_string_value("test3"), + create_string_value("test4"), + ]; + + assert!(as_bytes(&empty_values, &mut ctx).is_err()); + assert!(as_bytes(&too_many_values, &mut ctx).is_err()); + + assert!(append(&empty_values, &mut ctx).is_err()); + assert!(append(&too_many_values, &mut ctx).is_err()); + } + + #[test] + fn test_edge_cases() { + let mut ctx = Context::new(); + + // Test with very long string + let long_string = "a".repeat(1000); + let values = vec![create_string_value(&long_string)]; + + assert!(length(&values, &mut ctx).is_ok()); + assert!(reverse(&values, &mut ctx).is_ok()); + assert!(uppercase(&values, &mut ctx).is_ok()); + + // Test with special characters + let special_string = "!@#$%^&*(){}[]|\\:;\"'<>,.?/~`"; + let values = vec![create_string_value(special_string)]; + + assert!(length(&values, &mut ctx).is_ok()); + assert!(reverse(&values, &mut ctx).is_ok()); + assert!(uppercase(&values, &mut ctx).is_ok()); + + // Test with unicode characters + let unicode_string = "🦀🚀✨🎉"; + let values = vec![create_string_value(unicode_string)]; + + assert!(length(&values, &mut ctx).is_ok()); + assert!(reverse(&values, &mut ctx).is_ok()); + assert!(chars(&values, &mut ctx).is_ok()); + } + + #[test] + fn test_boundary_conditions() { + let mut ctx = Context::new(); + + // Test insert at various positions + let base_string = "hello"; + for i in 0..=5 { + let values = vec![ + create_string_value(base_string), + create_number_value(i as f64), + create_string_value("X"), + ]; + let result = insert(&values, &mut ctx); + assert!(result.is_ok(), "Insert at position {} should work", i); + } + + // Test remove with edge cases + let values = vec![ + create_string_value("hello"), + create_number_value(0.0), + create_number_value(0.0), // Remove nothing + ]; + let result = remove(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("hello")); + + // Test remove entire string + let values = vec![ + create_string_value("hello"), + create_number_value(0.0), + create_number_value(5.0), + ]; + let result = remove(&values, &mut ctx).unwrap(); + assert_eq!(result, create_string_value("")); + } +} diff --git a/src/main.rs b/src/main.rs index 38d5d63..48f79e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,11 +14,11 @@ use context::{Context, ContextEntry, ContextResult}; use error::RuntimeError; use futures_lite::StreamExt; use lapin::{options::BasicConsumeOptions, types::FieldTable}; -use locale::locale::Locale; use registry::FunctionStore; use tucana::shared::{Flow, NodeFunction, Value}; use crate::configuration::Config; +use crate::implementation::collect; fn handle_node_function( function: NodeFunction, @@ -177,9 +177,8 @@ async fn main() { load_env_file(); let config = Config::new(); - - let _locale = Locale::default(); - let store = FunctionStore::new(); + let mut store = FunctionStore::new(); + store.populate(collect()); let rabbitmq_client = Arc::new(RabbitmqClient::new(config.rabbitmq_url.as_str()).await);