From b14cf4ea5f8c0ffe95885fd15f45c62dbfc412b1 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 2 Jun 2025 18:13:28 +0200 Subject: [PATCH 01/16] feat: added implementations for number functions --- src/error/mod.rs | 24 +- src/implementation/mod.rs | 244 +----------- src/implementation/number/mod.rs | 639 +++++++++++++++++++++++++++++++ 3 files changed, 667 insertions(+), 240 deletions(-) create mode 100644 src/implementation/number/mod.rs diff --git a/src/error/mod.rs b/src/error/mod.rs index 8c5936f..ec183ae 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,21 @@ 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(name: &str, message: &str) -> Self { + Self { + name: name.to_string(), + message: message.to_string(), + suggestion: None, + } + } +} diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index ccf41ce..4f7f442 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,245 +1,11 @@ -use tucana::shared::{Value, value::Kind}; +use crate::registry::HandlerFn; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +pub mod number; 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()); - Ok(Value { - kind: Some(Kind::StringValue(value.to_string())), - }) + result } diff --git a/src/implementation/number/mod.rs b/src/implementation/number/mod.rs new file mode 100644 index 0000000..72ee4bd --- /dev/null +++ b/src/implementation/number/mod.rs @@ -0,0 +1,639 @@ +use std::f64; + +use tucana::shared::{value::Kind, Value}; + +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::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::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::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()); + }; + + if rhs == &0.0 { + return Err(RuntimeError::simple( + "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::default()); + }; + + if rhs == &0.0 { + return Err(RuntimeError::simple( + "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::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 square(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.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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + let value: f64 = match string_value.parse() { + Ok(result) => result, + Err(_) => return Err(RuntimeError::default()), + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} From 0823fbe31f618ae979038cd689ba7ba6c4e65b05 Mon Sep 17 00:00:00 2001 From: Raphael Date: Mon, 2 Jun 2025 18:13:39 +0200 Subject: [PATCH 02/16] dependencies: added rand --- Cargo.lock | 1 + Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 847771c..59a0140 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2276,6 +2276,7 @@ dependencies = [ "futures-lite 2.6.0", "lapin", "log", + "rand", "serde", "serde_json", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 74b3cd2..9adc641 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ 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" [dev-dependencies] tempfile = "3.19.1" From 07427569eaf9750e1949acf7ec5c70b5410ea714 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:30:10 +0200 Subject: [PATCH 03/16] feat: moved number mod and added tests for the functions --- src/implementation/mod.rs | 2 + src/implementation/number.rs | 1505 ++++++++++++++++++++++++++++++ src/implementation/number/mod.rs | 639 ------------- 3 files changed, 1507 insertions(+), 639 deletions(-) create mode 100644 src/implementation/number.rs delete mode 100644 src/implementation/number/mod.rs diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index 4f7f442..6378948 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,11 +1,13 @@ use crate::registry::HandlerFn; +pub mod boolean; pub mod number; pub fn collect() -> Vec<(&'static str, HandlerFn)> { let mut result = vec![]; result.extend(number::collect_number_functions()); + result.extend(boolean::collect_boolean_functions()); result } diff --git a/src/implementation/number.rs b/src/implementation/number.rs new file mode 100644 index 0000000..90bb3cd --- /dev/null +++ b/src/implementation/number.rs @@ -0,0 +1,1505 @@ +use std::f64; + +use tucana::shared::{value::Kind, Value}; + +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::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::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::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()); + }; + + if rhs == &0.0 { + return Err(RuntimeError::simple( + "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::default()); + }; + + if rhs == &0.0 { + return Err(RuntimeError::simple( + "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::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 square(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.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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + let value: f64 = match string_value.parse() { + Ok(result) => result, + Err(_) => return Err(RuntimeError::default()), + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + Ok(Value { + kind: Some(Kind::BoolValue(lhs == rhs)), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::context::Context; + use tucana::shared::{value::Kind, Value}; + + // 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_devide_success() { + let mut ctx = Context::new(); + let values = vec![create_number_value(15.0), create_number_value(3.0)]; + let result = devide(&values, &mut ctx).unwrap(); + + match result.kind { + Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), + _ => panic!("Expected NumberValue"), + } + } + + #[test] + fn test_devide_by_zero_exception() { + let mut ctx = Context::new(); + let values = vec![create_number_value(10.0), create_number_value(0.0)]; + let result = devide(&values, &mut ctx); + assert!(result.is_err()); + } + + #[test] + fn test_devide_runtime_exception() { + let mut ctx = Context::new(); + let values = vec![ + create_string_value("not_a_number"), + create_number_value(2.0), + ]; + let result = devide(&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/number/mod.rs b/src/implementation/number/mod.rs deleted file mode 100644 index 72ee4bd..0000000 --- a/src/implementation/number/mod.rs +++ /dev/null @@ -1,639 +0,0 @@ -use std::f64; - -use tucana::shared::{value::Kind, Value}; - -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::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::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::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()); - }; - - if rhs == &0.0 { - return Err(RuntimeError::simple( - "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::default()); - }; - - if rhs == &0.0 { - return Err(RuntimeError::simple( - "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::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 square(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.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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - let value: f64 = match string_value.parse() { - Ok(result) => result, - Err(_) => return Err(RuntimeError::default()), - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - 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::default()); - }; - - Ok(Value { - kind: Some(Kind::BoolValue(lhs == rhs)), - }) -} From fe1e7f50ec43aaebbcfc05dc3c19e7d891e1207c Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:31:04 +0200 Subject: [PATCH 04/16] feat: added boolean std functions --- src/implementation/boolean.rs | 515 ++++++++++++++++++++++++++++++++++ src/implementation/mod.rs | 1 - 2 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 src/implementation/boolean.rs diff --git a/src/implementation/boolean.rs b/src/implementation/boolean.rs new file mode 100644 index 0000000..0fb6078 --- /dev/null +++ b/src/implementation/boolean.rs @@ -0,0 +1,515 @@ +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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + 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::default()); + }; + + let bool: bool = match text.to_lowercase().parse() { + Ok(value) => value, + Err(_) => return Err(RuntimeError::default()), + }; + + 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::default()); + }; + + 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::default()); + }; + + 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 6378948..0d17310 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -7,7 +7,6 @@ pub fn collect() -> Vec<(&'static str, HandlerFn)> { let mut result = vec![]; result.extend(number::collect_number_functions()); - result.extend(boolean::collect_boolean_functions()); result } From e91ea008ae8f22f5dda05f5e24dc513bee58a888 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:32:09 +0200 Subject: [PATCH 05/16] fix: removed boolean --- src/implementation/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index 6378948..208a29c 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,6 +1,5 @@ use crate::registry::HandlerFn; -pub mod boolean; pub mod number; pub fn collect() -> Vec<(&'static str, HandlerFn)> { From e93f771c4b505fe05a8034342ee5f6d880dce7ad Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:32:27 +0200 Subject: [PATCH 06/16] fix: removed boolean --- src/implementation/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index 6378948..4f7f442 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,13 +1,11 @@ use crate::registry::HandlerFn; -pub mod boolean; pub mod number; pub fn collect() -> Vec<(&'static str, HandlerFn)> { let mut result = vec![]; result.extend(number::collect_number_functions()); - result.extend(boolean::collect_boolean_functions()); result } From cccab4bf089246cdc4dd8e1d79e3b46c0bafc1a6 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:44:04 +0200 Subject: [PATCH 07/16] feat: added error messages --- src/error/mod.rs | 10 +- src/implementation/number.rs | 240 +++++++++++++++++++++++++++++------ 2 files changed, 211 insertions(+), 39 deletions(-) diff --git a/src/error/mod.rs b/src/error/mod.rs index ec183ae..3465554 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -27,11 +27,19 @@ impl RuntimeError { } } - pub fn simple(name: &str, message: &str) -> Self { + 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/number.rs b/src/implementation/number.rs index 90bb3cd..98fab63 100644 --- a/src/implementation/number.rs +++ b/src/implementation/number.rs @@ -54,7 +54,13 @@ fn add(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -69,7 +75,13 @@ fn multiply(values: &[Value], _ctx: &mut Context) -> Result kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -84,7 +96,13 @@ fn substract(values: &[Value], _ctx: &mut Context) -> Result Result { kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; if rhs == &0.0 { - return Err(RuntimeError::simple( + return Err(RuntimeError::simple_str( "DivisionByZero", "You cannot divide by zero", )); @@ -121,11 +145,17 @@ fn modulo(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; if rhs == &0.0 { - return Err(RuntimeError::simple( + return Err(RuntimeError::simple_str( "DivisionByZero", "You cannot divide by zero", )); @@ -141,7 +171,10 @@ fn abs(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); }; Ok(Value { @@ -154,7 +187,10 @@ fn is_positive(values: &[Value], _ctx: &mut Context) -> Result Result Result kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -197,7 +245,10 @@ fn is_zero(values: &[Value], _ctx: &mut Context) -> Result kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); }; Ok(Value { @@ -210,7 +261,10 @@ fn square(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a number as argument but received {:?}", values), + )); }; Ok(Value { @@ -225,7 +279,13 @@ fn exponential(values: &[Value], _ctx: &mut Context) -> Result Result kind: Some(Kind::NumberValue(decimal_places)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; let factor = 10_f64.powi(decimal_places.clone() as i32); @@ -275,7 +341,13 @@ fn round_down(values: &[Value], _ctx: &mut Context) -> Result Result { kind: Some(Kind::NumberValue(decimal_places)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; let factor = 10_f64.powi(decimal_places.clone() as i32); @@ -307,7 +385,10 @@ fn square_root(values: &[Value], _ctx: &mut Context) -> Result Result { kind: Some(Kind::NumberValue(root)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -337,7 +424,13 @@ fn log(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(log)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -350,7 +443,10 @@ fn ln(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -363,12 +459,20 @@ fn from_text(values: &[Value], _ctx: &mut Context) -> Result result, - Err(_) => return Err(RuntimeError::default()), + Err(_) => { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse string as number: {}", string_value), + )) + } }; Ok(Value { @@ -381,7 +485,10 @@ fn as_text(values: &[Value], _ctx: &mut Context) -> Result kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -396,7 +503,13 @@ fn min(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -411,7 +524,13 @@ fn max(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -424,7 +543,10 @@ fn negate(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -439,7 +561,13 @@ fn random(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(max)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -454,7 +582,10 @@ fn sin(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -467,7 +598,10 @@ fn cos(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -480,7 +614,10 @@ fn tan(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -493,7 +630,10 @@ fn arcsin(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -506,7 +646,10 @@ fn arccos(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -519,7 +662,10 @@ fn arctan(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -531,7 +677,10 @@ fn sinh(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -544,7 +693,10 @@ fn cosh(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected one number as argument but received {:?}", values), + )); }; Ok(Value { @@ -561,7 +713,13 @@ fn clamp(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::NumberValue(max)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected three numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { @@ -576,7 +734,13 @@ fn is_equal(values: &[Value], _ctx: &mut Context) -> Result kind: Some(Kind::NumberValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers as arguments but received {:?}", + values + ), + )); }; Ok(Value { From f0c253249ea9b11d86e36a1ff7fb3210022981de Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:45:37 +0200 Subject: [PATCH 08/16] feat: added error description for boolean --- src/implementation/boolean.rs | 37 ++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/implementation/boolean.rs b/src/implementation/boolean.rs index 0fb6078..6b45963 100644 --- a/src/implementation/boolean.rs +++ b/src/implementation/boolean.rs @@ -17,7 +17,10 @@ fn as_number(values: &[Value], _ctx: &mut Context) -> Result Result kind: Some(Kind::BoolValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple(format!( + "Expected a boolean value but received {:?}", + values + ))); }; Ok(Value { @@ -43,7 +49,10 @@ fn from_number(values: &[Value], _ctx: &mut Context) -> Result Result value, - Err(_) => return Err(RuntimeError::default()), + Err(_) => { + return Err(RuntimeError::simple(format!( + "Failed to parse boolean from string: {:?}", + text + ))) + } }; Ok(Value { @@ -78,7 +95,10 @@ fn is_equal(values: &[Value], _ctx: &mut Context) -> Result kind: Some(Kind::BoolValue(rhs)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple(format!( + "Expected two boolean values but received {:?}", + values + ))); }; Ok(Value { @@ -91,7 +111,10 @@ fn negate(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::BoolValue(value)), }] = values else { - return Err(RuntimeError::default()); + return Err(RuntimeError::simple(format!( + "Expected a boolean value but received {:?}", + values + ))); }; Ok(Value { From 9b67ba03b62de0d146b1cdbfa073b87a2ec78a36 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 3 Jun 2025 17:54:04 +0200 Subject: [PATCH 09/16] fix: added missing fields --- src/implementation/boolean.rs | 56 +++++++++++++++++------------------ src/implementation/mod.rs | 2 ++ 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/implementation/boolean.rs b/src/implementation/boolean.rs index 6b45963..528b34e 100644 --- a/src/implementation/boolean.rs +++ b/src/implementation/boolean.rs @@ -17,10 +17,10 @@ fn as_number(values: &[Value], _ctx: &mut Context) -> Result Result kind: Some(Kind::BoolValue(value)), }] = values else { - return Err(RuntimeError::simple(format!( - "Expected a boolean value but received {:?}", - values - ))); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a boolean value but received {:?}", values), + )); }; Ok(Value { @@ -49,10 +49,10 @@ fn from_number(values: &[Value], _ctx: &mut Context) -> Result Result value, Err(_) => { - return Err(RuntimeError::simple(format!( - "Failed to parse boolean from string: {:?}", - text - ))) + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )) } }; @@ -95,10 +95,10 @@ fn is_equal(values: &[Value], _ctx: &mut Context) -> Result kind: Some(Kind::BoolValue(rhs)), }] = values else { - return Err(RuntimeError::simple(format!( - "Expected two boolean values but received {:?}", - values - ))); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected two boolean values but received {:?}", values), + )); }; Ok(Value { @@ -111,10 +111,10 @@ fn negate(values: &[Value], _ctx: &mut Context) -> Result { kind: Some(Kind::BoolValue(value)), }] = values else { - return Err(RuntimeError::simple(format!( - "Expected a boolean value but received {:?}", - values - ))); + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a boolean value but received {:?}", values), + )); }; Ok(Value { diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index 4f7f442..6378948 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,11 +1,13 @@ use crate::registry::HandlerFn; +pub mod boolean; pub mod number; pub fn collect() -> Vec<(&'static str, HandlerFn)> { let mut result = vec![]; result.extend(number::collect_number_functions()); + result.extend(boolean::collect_boolean_functions()); result } From a5206c214a5f175c207e57a75d1e69d3db0ad84c Mon Sep 17 00:00:00 2001 From: Raphael Date: Wed, 4 Jun 2025 19:51:25 +0200 Subject: [PATCH 10/16] feat: added most of the mvp text function implementations --- src/implementation/mod.rs | 1 + src/implementation/text.rs | 661 +++++++++++++++++++++++++++++++++++++ 2 files changed, 662 insertions(+) create mode 100644 src/implementation/text.rs diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index 6378948..fa5ae6b 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -2,6 +2,7 @@ use crate::registry::HandlerFn; pub mod boolean; pub mod number; +pub mod text; pub fn collect() -> Vec<(&'static str, HandlerFn)> { let mut result = vec![]; diff --git a/src/implementation/text.rs b/src/implementation/text.rs new file mode 100644 index 0000000..687cd9e --- /dev/null +++ b/src/implementation/text.rs @@ -0,0 +1,661 @@ +use tucana::shared::{value::Kind, ListValue, Value}; + +use crate::{context::Context, error::RuntimeError}; + +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 two numbers as arguments 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 two numbers as arguments 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 two numbers as arguments 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 upper = String::from(&word[..0]).to_uppercase(); + return String::from(upper + &word[1..]); + }) + .collect(); + + 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 two numbers as arguments 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 two numbers as arguments 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 two numbers as arguments 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 two numbers as arguments 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 two numbers as arguments but received {:?}", + values + ), + )); + }; + + 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 two numbers as 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 arguments 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 numbers 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 two numbers 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 two numbers as arguments 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 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 two numbers 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 two numbers 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 two numbers 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 two numbers as arguments 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 two numbers as arguments 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 numbers 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 numbers 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 numbers 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 two numbers as arguments 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 numbers 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 numbers 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 two numbers as arguments 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, + })), + }) +} + +// todo!("from_ascii") From 620efe341f3d30c5e63fdd97106a9a10b5f4a503 Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 5 Jun 2025 16:26:01 +0200 Subject: [PATCH 11/16] feat: added missing text functions (exepct encode & decode) --- src/implementation/mod.rs | 3 + src/implementation/text.rs | 1006 +++++++++++++++++++++++++++++++++++- src/main.rs | 7 +- 3 files changed, 1009 insertions(+), 7 deletions(-) diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index fa5ae6b..281b40f 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::registry::HandlerFn; pub mod boolean; @@ -9,6 +11,7 @@ pub fn collect() -> Vec<(&'static str, HandlerFn)> { result.extend(number::collect_number_functions()); result.extend(boolean::collect_boolean_functions()); + result.extend(text::collect_text_functions()); result } diff --git a/src/implementation/text.rs b/src/implementation/text.rs index 687cd9e..ecb6fcd 100644 --- a/src/implementation/text.rs +++ b/src/implementation/text.rs @@ -1,6 +1,40 @@ +use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; use tucana::shared::{value::Kind, ListValue, Value}; -use crate::{context::Context, error::RuntimeError}; +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 { @@ -73,10 +107,17 @@ fn capitalize(values: &[Value], _ctx: &mut Context) -> Result>() + .join(" "); Ok(Value { kind: Some(Kind::StringValue(capitalized_value)), @@ -195,6 +236,16 @@ fn at(values: &[Value], _ctx: &mut Context) -> Result { )); }; + 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); @@ -659,3 +710,950 @@ fn to_ascii(values: &[Value], _ctx: &mut Context) -> Result } // todo!("from_ascii") + +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 two numbers as arguments 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 { + todo!("not implemented") +} + +fn decode(values: &[Value], _ctx: &mut Context) -> Result { + todo!("not implemented") +} + +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 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::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_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 f7c78b2..5e665a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,10 +11,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::implementation::collect; + fn handle_node_function( function: NodeFunction, store: &FunctionStore, @@ -165,8 +166,8 @@ fn handle_message(message: Message, store: &FunctionStore) -> Result Date: Fri, 6 Jun 2025 16:19:11 +0200 Subject: [PATCH 12/16] dependencies: added base64 --- Cargo.lock | 1 + Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 59a0140..d9d5e04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2272,6 +2272,7 @@ dependencies = [ name = "taurus" version = "0.1.0" dependencies = [ + "base64", "code0-flow", "futures-lite 2.6.0", "lapin", diff --git a/Cargo.toml b/Cargo.toml index 9adc641..5e80da4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ toml = "0.8.0" log = "0.4.27" futures-lite = "2.6.0" rand = "0.9.1" +base64 = "0.22.1" [dev-dependencies] tempfile = "3.19.1" From ff30ac1307cbfb03c7edc3624c30a67e08740ad5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 6 Jun 2025 16:19:22 +0200 Subject: [PATCH 13/16] feat: added encode and decode function implementation --- src/implementation/text.rs | 136 ++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) diff --git a/src/implementation/text.rs b/src/implementation/text.rs index ecb6fcd..8f49de1 100644 --- a/src/implementation/text.rs +++ b/src/implementation/text.rs @@ -1,4 +1,5 @@ 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)> { @@ -758,11 +759,81 @@ fn from_ascii(values: &[Value], _ctx: &mut Context) -> Result Result { - todo!("not implemented") + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(encoding)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers 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 { - todo!("not implemented") + let [Value { + kind: Some(Kind::StringValue(value)), + }, Value { + kind: Some(Kind::StringValue(encoding)), + }] = values + else { + return Err(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!( + "Expected two numbers 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 { @@ -1362,6 +1433,67 @@ mod tests { 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(); From 48b1b87ddb220c7820006de23067a78c8668e23a Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 6 Jun 2025 16:26:55 +0200 Subject: [PATCH 14/16] fix: correct error messages for text functions --- src/implementation/text.rs | 68 +++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/implementation/text.rs b/src/implementation/text.rs index 8f49de1..4d2a724 100644 --- a/src/implementation/text.rs +++ b/src/implementation/text.rs @@ -44,10 +44,7 @@ fn as_bytes(values: &[Value], _ctx: &mut Context) -> Result else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), + format!("Expected one string as argument but received {:?}", values), )); }; @@ -72,7 +69,7 @@ fn byte_size(values: &[Value], _ctx: &mut Context) -> Result Result Result Result Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -203,7 +200,7 @@ fn chars(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -231,7 +228,7 @@ fn at(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string followed by one number as arguments but received {:?}", values ), )); @@ -273,7 +270,7 @@ fn trim(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an arguments but received {:?}", values ), )); @@ -293,10 +290,7 @@ fn append(values: &[Value], _ctx: &mut Context) -> Result { else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), + format!("Expected two numbers as argument but received {:?}", values), )); }; @@ -315,7 +309,7 @@ fn prepend(values: &[Value], _ctx: &mut Context) -> Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); @@ -338,7 +332,7 @@ fn insert(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected string, number, and string as arguments but received {:?}", values ), )); @@ -361,7 +355,7 @@ fn length(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -384,7 +378,7 @@ fn remove(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string followed by two numbers as arguments but received {:?}", values ), )); @@ -416,7 +410,7 @@ fn replace(values: &[Value], _ctx: &mut Context) -> Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected three strings as arguments but received {:?}", values ), )); @@ -441,7 +435,7 @@ fn replace_first(values: &[Value], _ctx: &mut Context) -> Result Result Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -525,7 +519,7 @@ fn octal(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -552,7 +546,7 @@ fn index_of(values: &[Value], _ctx: &mut Context) -> Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); @@ -580,7 +574,7 @@ fn contains(values: &[Value], _ctx: &mut Context) -> Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); @@ -601,7 +595,7 @@ fn split(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); @@ -627,7 +621,7 @@ fn reverse(values: &[Value], _ctx: &mut Context) -> Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -650,7 +644,7 @@ fn starts_with(values: &[Value], _ctx: &mut Context) -> Result Result Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected one string as an argument but received {:?}", values ), )); @@ -710,8 +704,6 @@ fn to_ascii(values: &[Value], _ctx: &mut Context) -> Result }) } -// todo!("from_ascii") - fn from_ascii(values: &[Value], _ctx: &mut Context) -> Result { let [Value { kind: Some(Kind::ListValue(list)), @@ -720,7 +712,7 @@ fn from_ascii(values: &[Value], _ctx: &mut Context) -> Result Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); @@ -799,7 +791,7 @@ fn decode(values: &[Value], _ctx: &mut Context) -> Result { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); @@ -846,7 +838,7 @@ fn is_equal(values: &[Value], _ctx: &mut Context) -> Result return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( - "Expected two numbers as arguments but received {:?}", + "Expected two strings as arguments but received {:?}", values ), )); From 21e534eca6ab06ca0f9c4daa73eaa2a16d02ab4f Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 6 Jun 2025 16:35:06 +0200 Subject: [PATCH 15/16] fix: removed locale from main function --- src/main.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index ba109c7..48f79e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,8 @@ use lapin::{options::BasicConsumeOptions, types::FieldTable}; use registry::FunctionStore; use tucana::shared::{Flow, NodeFunction, Value}; -use crate::implementation::collect; use crate::configuration::Config; +use crate::implementation::collect; fn handle_node_function( function: NodeFunction, @@ -177,9 +177,7 @@ 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); From 07c3d9cf3d7ec1a25256189120fcea09dc41572b Mon Sep 17 00:00:00 2001 From: Raphael Date: Sat, 7 Jun 2025 16:18:48 +0200 Subject: [PATCH 16/16] ref: fixed typo --- src/implementation/mod.rs | 2 - src/implementation/number.rs | 397 +++++++++++++++++++++-------------- 2 files changed, 243 insertions(+), 156 deletions(-) diff --git a/src/implementation/mod.rs b/src/implementation/mod.rs index 281b40f..ed19860 100644 --- a/src/implementation/mod.rs +++ b/src/implementation/mod.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::registry::HandlerFn; pub mod boolean; diff --git a/src/implementation/number.rs b/src/implementation/number.rs index 98fab63..49163be 100644 --- a/src/implementation/number.rs +++ b/src/implementation/number.rs @@ -1,6 +1,6 @@ use std::f64; -use tucana::shared::{value::Kind, Value}; +use tucana::shared::{Value, value::Kind}; use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; @@ -9,7 +9,7 @@ pub fn collect_number_functions() -> Vec<(&'static str, HandlerFn)> { ("std::number::add", add), ("std::number::multiply", multiply), ("std::number::substract", substract), - ("std::number::devide", devide), + ("std::number::divide", divide), ("std::number::modulo", modulo), ("std::number::abs", abs), ("std::number::is_positive", is_positive), @@ -48,11 +48,14 @@ pub fn collect_number_functions() -> Vec<(&'static str, HandlerFn)> { } fn add(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -69,11 +72,14 @@ fn add(values: &[Value], _ctx: &mut Context) -> Result { } fn multiply(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -90,11 +96,14 @@ fn multiply(values: &[Value], _ctx: &mut Context) -> Result } fn substract(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -110,12 +119,15 @@ fn substract(values: &[Value], _ctx: &mut Context) -> Result Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values +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", @@ -139,11 +151,14 @@ fn devide(values: &[Value], _ctx: &mut Context) -> Result { } fn modulo(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -167,9 +182,11 @@ fn modulo(values: &[Value], _ctx: &mut Context) -> Result { } fn abs(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -183,9 +200,11 @@ fn abs(values: &[Value], _ctx: &mut Context) -> Result { } fn is_positive(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -199,11 +218,14 @@ fn is_positive(values: &[Value], _ctx: &mut Context) -> Result Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -220,11 +242,14 @@ fn is_greater(values: &[Value], _ctx: &mut Context) -> Result Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -241,9 +266,11 @@ fn is_less(values: &[Value], _ctx: &mut Context) -> Result } fn is_zero(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -257,9 +284,11 @@ fn is_zero(values: &[Value], _ctx: &mut Context) -> Result } fn square(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -273,11 +302,14 @@ fn square(values: &[Value], _ctx: &mut Context) -> Result { } fn exponential(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(base)), - }, Value { - kind: Some(Kind::NumberValue(exponent)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(base)), + }, + Value { + kind: Some(Kind::NumberValue(exponent)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -312,11 +344,14 @@ fn infinity(_values: &[Value], _ctx: &mut Context) -> Result Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }, Value { - kind: Some(Kind::NumberValue(decimal_places)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(decimal_places)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -335,11 +370,14 @@ fn round_up(values: &[Value], _ctx: &mut Context) -> Result } fn round_down(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }, Value { - kind: Some(Kind::NumberValue(decimal_places)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(decimal_places)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -358,11 +396,14 @@ fn round_down(values: &[Value], _ctx: &mut Context) -> Result Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }, Value { - kind: Some(Kind::NumberValue(decimal_places)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(decimal_places)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -381,9 +422,11 @@ fn round(values: &[Value], _ctx: &mut Context) -> Result { } fn square_root(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -397,11 +440,14 @@ fn square_root(values: &[Value], _ctx: &mut Context) -> Result Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }, Value { - kind: Some(Kind::NumberValue(root)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(root)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -418,11 +464,14 @@ fn root(values: &[Value], _ctx: &mut Context) -> Result { } fn log(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }, Value { - kind: Some(Kind::NumberValue(log)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + Value { + kind: Some(Kind::NumberValue(log)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -439,9 +488,11 @@ fn log(values: &[Value], _ctx: &mut Context) -> Result { } fn ln(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -455,9 +506,11 @@ fn ln(values: &[Value], _ctx: &mut Context) -> Result { } fn from_text(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::StringValue(string_value)), - }] = values + let [ + Value { + kind: Some(Kind::StringValue(string_value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -471,7 +524,7 @@ fn from_text(values: &[Value], _ctx: &mut Context) -> Result Result Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -497,11 +552,14 @@ fn as_text(values: &[Value], _ctx: &mut Context) -> Result } fn min(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -518,11 +576,14 @@ fn min(values: &[Value], _ctx: &mut Context) -> Result { } fn max(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -539,9 +600,11 @@ fn max(values: &[Value], _ctx: &mut Context) -> Result { } fn negate(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -555,11 +618,14 @@ fn negate(values: &[Value], _ctx: &mut Context) -> Result { } fn random(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(min)), - }, Value { - kind: Some(Kind::NumberValue(max)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(min)), + }, + Value { + kind: Some(Kind::NumberValue(max)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -578,9 +644,11 @@ fn random(values: &[Value], _ctx: &mut Context) -> Result { } fn sin(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -594,9 +662,11 @@ fn sin(values: &[Value], _ctx: &mut Context) -> Result { } fn cos(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -610,9 +680,11 @@ fn cos(values: &[Value], _ctx: &mut Context) -> Result { } fn tan(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -626,9 +698,11 @@ fn tan(values: &[Value], _ctx: &mut Context) -> Result { } fn arcsin(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -642,9 +716,11 @@ fn arcsin(values: &[Value], _ctx: &mut Context) -> Result { } fn arccos(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -658,9 +734,11 @@ fn arccos(values: &[Value], _ctx: &mut Context) -> Result { } fn arctan(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -673,9 +751,11 @@ fn arctan(values: &[Value], _ctx: &mut Context) -> Result { }) } fn sinh(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -689,9 +769,11 @@ fn sinh(values: &[Value], _ctx: &mut Context) -> Result { } fn cosh(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(value)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(value)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -705,13 +787,17 @@ fn cosh(values: &[Value], _ctx: &mut Context) -> Result { } 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 + 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", @@ -728,11 +814,14 @@ fn clamp(values: &[Value], _ctx: &mut Context) -> Result { } fn is_equal(values: &[Value], _ctx: &mut Context) -> Result { - let [Value { - kind: Some(Kind::NumberValue(lhs)), - }, Value { - kind: Some(Kind::NumberValue(rhs)), - }] = values + let [ + Value { + kind: Some(Kind::NumberValue(lhs)), + }, + Value { + kind: Some(Kind::NumberValue(rhs)), + }, + ] = values else { return Err(RuntimeError::simple( "InvalidArgumentRuntimeError", @@ -752,7 +841,7 @@ fn is_equal(values: &[Value], _ctx: &mut Context) -> Result mod tests { use super::*; use crate::context::Context; - use tucana::shared::{value::Kind, Value}; + use tucana::shared::{Value, value::Kind}; // Helper function to create a number value fn create_number_value(num: f64) -> Value { @@ -853,10 +942,10 @@ mod tests { } #[test] - fn test_devide_success() { + fn test_divide_success() { let mut ctx = Context::new(); let values = vec![create_number_value(15.0), create_number_value(3.0)]; - let result = devide(&values, &mut ctx).unwrap(); + let result = divide(&values, &mut ctx).unwrap(); match result.kind { Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), @@ -865,21 +954,21 @@ mod tests { } #[test] - fn test_devide_by_zero_exception() { + 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 = devide(&values, &mut ctx); + let result = divide(&values, &mut ctx); assert!(result.is_err()); } #[test] - fn test_devide_runtime_exception() { + 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 = devide(&values, &mut ctx); + let result = divide(&values, &mut ctx); assert!(result.is_err()); }