From ef0e5c6407cd2dd3ba6c40f26f90f4c11985ef4c Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Fri, 7 Nov 2025 22:58:39 +0100 Subject: [PATCH 01/10] feat: wip if-else logic --- src/context/signal.rs | 10 +++--- src/executor/mod.rs | 2 +- src/implementation/control.rs | 66 +++++++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/context/signal.rs b/src/context/signal.rs index 9e0cf30..0c40add 100644 --- a/src/context/signal.rs +++ b/src/context/signal.rs @@ -6,20 +6,20 @@ pub enum Signal { // Will be signaled if a function has been executed successfully Success(Value), // Will be signaled if - // - a function recieves wrong parameter + // - a function receives wrong parameter // - a function throws an error - // - taurus itself throwns an error - // - will stop the execution of the flow completly + // - taurus itself throws an error + // - will stop the execution of the flow completely Failure(RuntimeError), // Will be signaled if the `return` function has been executed // - will break the current context and return the value to the upper node Return(Value), // Will be signaled if the `respond` function has been executed - // - will stop the execution of the flow completly + // - will stop the execution of the flow completely // - will return the value to the adapter Respond(Value), // Will be signaled if the `stop` function has been executed - // - will stop the execution of the flow completly + // - will stop the execution of the flow completely Stop, } diff --git a/src/executor/mod.rs b/src/executor/mod.rs index cf3f844..f318d2c 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -106,7 +106,7 @@ impl<'a> Executor<'a> { Signal::Success(value.clone()) } tucana::shared::node_value::Value::ReferenceValue(_reference_value) => { - todo!("implement reference values!") + unimplemented!("implement reference values!") } tucana::shared::node_value::Value::NodeFunctionId(id) => Executor::execute(self, *id), } diff --git a/src/implementation/control.rs b/src/implementation/control.rs index be49084..4b93305 100644 --- a/src/implementation/control.rs +++ b/src/implementation/control.rs @@ -1,12 +1,14 @@ -use tucana::shared::Value; - use crate::context::signal::Signal; use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use tucana::shared::Value; +use tucana::shared::value::Kind; pub fn collect_control_functions() -> Vec<(&'static str, HandlerFn)> { vec![ ("std::control::stop", stop), ("std::control::return", r#return), + ("std::control::if", r#if), + ("std::control::if_else", if_else), ] } @@ -24,3 +26,63 @@ fn r#return(values: &[Value], _ctx: &mut Context) -> Signal { Signal::Return(Value { kind: kind.clone() }) } + +fn r#if(values: &[Value], _ctx: &mut Context) -> Signal { + let [ + Value { + kind: Some(Kind::StringValue(text)), + }, + ] = values + else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a string value but received {:?}", values), + )); + }; + + let bool: bool = match text.to_lowercase().parse() { + Ok(value) => value, + Err(_) => { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )); + } + }; + + if bool { + unimplemented!() + } else { + unimplemented!() + } +} + +fn if_else(values: &[Value], _ctx: &mut Context) -> Signal { + let [ + Value { + kind: Some(Kind::StringValue(text)), + }, + ] = values + else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected a string value but received {:?}", values), + )); + }; + + let bool: bool = match text.to_lowercase().parse() { + Ok(value) => value, + Err(_) => { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Failed to parse boolean from string: {:?}", text), + )); + } + }; + + if bool { + unimplemented!() + } else { + unimplemented!() + } +} \ No newline at end of file From 603b264d01f346e5c90170dbcd0a82550cdfca67 Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sat, 8 Nov 2025 16:52:26 +0100 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20wip=20added=20`Lazy`=20and=20`Eag?= =?UTF-8?q?er`=20functions=20to=20=C3=ACmplement=20if-else=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context/argument.rs | 15 +++ src/context/executor.rs | 113 ++++++++++++++++++ src/context/mod.rs | 3 + src/{registry/mod.rs => context/registry.rs} | 23 +++- src/executor/mod.rs | 114 ------------------- src/implementation/control.rs | 16 +-- src/implementation/number.rs | 4 +- 7 files changed, 160 insertions(+), 128 deletions(-) create mode 100644 src/context/argument.rs create mode 100644 src/context/executor.rs rename src/{registry/mod.rs => context/registry.rs} (52%) delete mode 100644 src/executor/mod.rs diff --git a/src/context/argument.rs b/src/context/argument.rs new file mode 100644 index 0000000..98a248a --- /dev/null +++ b/src/context/argument.rs @@ -0,0 +1,15 @@ +#[derive(Clone, Debug)] +pub enum Argument { + // Eval => Evaluated Value + // - can be consumed directly by a function + Eval(tucana::shared::Value), + // Thunk of NodeFunction identifier + // - used for lazy execution of nodes + Thunk(i64) +} + +#[derive(Clone, Copy, Debug)] +pub enum ParameterNode { + Eager, + Lazy +} \ No newline at end of file diff --git a/src/context/executor.rs b/src/context/executor.rs new file mode 100644 index 0000000..1fc8728 --- /dev/null +++ b/src/context/executor.rs @@ -0,0 +1,113 @@ +use crate::context::Context; +use crate::context::signal::Signal; +use crate::error::RuntimeError; +use std::collections::HashMap; +use tucana::shared::{NodeFunction, NodeParameter}; +use crate::context::argument::{Argument, ParameterNode}; +use crate::context::registry::FunctionStore; + +pub struct Executor<'a> { + functions: &'a FunctionStore, + nodes: HashMap, + context: Context, +} + +type HandleNodeParameterFn = fn(&mut Executor, node_parameter: &NodeParameter) -> Signal; + +impl<'a> Executor<'a> { + pub fn new( + functions: &'a FunctionStore, + nodes: HashMap, + context: Context, + ) -> Self { + Executor { + functions, + nodes, + context, + } + } + + pub fn execute(&mut self, starting_node_id: i64) -> Signal { + let mut current_node_id = starting_node_id; + + loop { + let node = match self.nodes.get(¤t_node_id) { + None => { + return Signal::Failure(RuntimeError::simple_str( + "NodeNotFound", + "The node with the id was not found", + )); + } + Some(n) => n.clone(), + }; + + + let entry = match self.functions.get(node.runtime_function_id.as_str()) { + None => { + return Signal::Failure(RuntimeError::simple_str("FunctionNotFound","The function was not found")) + }, + Some(f) => f, + }; + + let mut args: Vec = Vec::with_capacity(node.parameters.len()); + for parameter in &node.parameters { + let node_value = match ¶meter.value { + Some(v) => v, + None => return Signal::Failure(RuntimeError::simple_str("NodeValueNotFound","Missing parameter value")), + }; + let value = match &node_value.value { + Some(v) => v, + None => return Signal::Failure(RuntimeError::simple_str("NodeValueNotFound","Missing inner value")), + }; + + match value { + tucana::shared::node_value::Value::LiteralValue(val) => { + args.push(Argument::Eval(val.clone())) + } + tucana::shared::node_value::Value::ReferenceValue(_r) => { + unimplemented!("ReferenceValue") + } + tucana::shared::node_value::Value::NodeFunctionId(id) => { + args.push(Argument::Thunk(*id)) + } + } + } + + + // Eagerly evaluate args that the function *declares* as Eager + for (i, a) in args.iter_mut().enumerate() { + let mode = entry.param_modes.get(i).copied().unwrap_or(ParameterNode::Eager); + if matches!(mode, ParameterNode::Eager) { + if let Argument::Thunk(id) = *a { + match self.execute(id) { + Signal::Success(v) => *a = Argument::Eval(v), + // propagate control flow immediately + s @ (Signal::Failure(_) | Signal::Return(_) | Signal::Respond(_) | Signal::Stop) => return s, + } + } + } + } + + // Provide a runner for Lazy params + let mut run = |node_id: i64| self.execute(node_id); + + // Call the handler (no special cases anywhere) + let result = (entry.handler)(&args, &mut self.context, &mut run); + + match result { + Signal::Success(value) => { + if let Some(next_node_id) = node.next_node_id { + current_node_id = next_node_id; + continue; + } else { + return Signal::Success(value); + } + } + Signal::Failure(e) => return Signal::Failure(e), + Signal::Return(v) => return Signal::Return(v), + Signal::Respond(v) => return Signal::Respond(v), + Signal::Stop => return Signal::Stop, + } + } + } +} diff --git a/src/context/mod.rs b/src/context/mod.rs index 89d720c..79a345c 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -1,5 +1,8 @@ pub mod context; pub mod signal; +pub mod argument; +pub mod executor; +mod registry; use crate::error::RuntimeError; use std::{ diff --git a/src/registry/mod.rs b/src/context/registry.rs similarity index 52% rename from src/registry/mod.rs rename to src/context/registry.rs index b523129..e4fcc54 100644 --- a/src/registry/mod.rs +++ b/src/context/registry.rs @@ -1,13 +1,26 @@ use crate::context::Context; +use crate::context::argument::{Argument, ParameterNode}; use crate::context::signal::Signal; use std::collections::HashMap; -use tucana::shared::Value; -pub type HandlerFn = fn(&[Value], &mut Context) -> Signal; +/// HandlerFm +/// - For eager params, the executor will already convert them to Argument::Eval(Value). +/// - For lazy params, the executor will pass Argument::Thunk(node_id). +/// - If a handler wants to execute a lazy arg, it calls run(node_id). +pub type HandlerFn = fn( + args: &[Argument], + ctx: &mut Context, + run: &mut dyn FnMut(i64) -> Signal, +) -> Signal; + +pub struct HandlerFunctionEntry { + pub handler: HandlerFn, + pub param_modes: Vec, +} /// Holds all registered handlers. pub struct FunctionStore { - functions: HashMap, + functions: HashMap, } impl Default for FunctionStore { @@ -25,12 +38,12 @@ impl FunctionStore { } /// Look up a handler by its ID. - pub fn get(&self, id: &str) -> Option<&HandlerFn> { + pub fn get(&self, id: &str) -> Option<&HandlerFunctionEntry> { self.functions.get(id) } /// Execute all the registration closures to populate the map. - pub fn populate(&mut self, regs: Vec<(&'static str, HandlerFn)>) { + pub fn populate(&mut self, regs: Vec<(&'static str, HandlerFunctionEntry)>) { for (id, func) in regs { self.functions.insert(id.to_string(), func); } diff --git a/src/executor/mod.rs b/src/executor/mod.rs deleted file mode 100644 index f318d2c..0000000 --- a/src/executor/mod.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::context::Context; -use crate::context::signal::Signal; -use crate::error::RuntimeError; -use crate::registry::FunctionStore; -use std::collections::HashMap; -use tucana::shared::{NodeFunction, NodeParameter}; - -pub struct Executor<'a> { - functions: &'a FunctionStore, - nodes: HashMap, - context: Context, -} - -type HandleNodeParameterFn = fn(&mut Executor, node_parameter: &NodeParameter) -> Signal; - -impl<'a> Executor<'a> { - pub fn new( - functions: &'a FunctionStore, - nodes: HashMap, - context: Context, - ) -> Self { - Executor { - functions, - nodes, - context, - } - } - - pub fn execute(&mut self, starting_node_id: i64) -> Signal { - let mut current_node_id = starting_node_id; - - loop { - let node = match self.nodes.get(¤t_node_id) { - None => { - return Signal::Failure(RuntimeError::simple_str( - "NodeNotFound", - "The node with the id was not found", - )); - } - Some(n) => n.clone(), - }; - - let mut parameters = Vec::new(); - - for node_parameter in &node.parameters { - match Executor::handle_node_parameter(self, node_parameter) { - Signal::Success(value) | Signal::Return(value) => { - parameters.push(value.clone()) - } - Signal::Failure(error) => return Signal::Failure(error), - Signal::Stop => return Signal::Stop, - Signal::Respond(value) => return Signal::Respond(value), - } - } - - let execution_result = match self.functions.get(node.runtime_function_id.as_str()) { - Some(handler) => handler(¶meters, &mut self.context), - None => { - return Signal::Failure(RuntimeError::simple_str( - "FunctionNotFound", - "The function was not found", - )); - } - }; - - match execution_result { - Signal::Success(value) => { - if let Some(next_node_id) = node.next_node_id { - current_node_id = next_node_id; - continue; - } else { - return Signal::Success(value); - } - } - Signal::Failure(runtime_error) => return Signal::Failure(runtime_error), - Signal::Return(value) => return Signal::Return(value), - Signal::Respond(value) => return Signal::Respond(value), - Signal::Stop => return Signal::Stop, - } - } - } - - fn handle_node_parameter(&mut self, node_parameter: &NodeParameter) -> Signal { - let node_value = match &node_parameter.value { - Some(v) => v, - None => { - return Signal::Failure(RuntimeError::simple_str( - "NodeValueNotFound", - "An error occurred while executing a flow!", - )); - } - }; - - let value = match &node_value.value { - Some(v) => v, - None => { - return Signal::Failure(RuntimeError::simple_str( - "NodeValueNotFound", - "An error occurred while executing a flow!", - )); - } - }; - - match value { - tucana::shared::node_value::Value::LiteralValue(value) => { - Signal::Success(value.clone()) - } - tucana::shared::node_value::Value::ReferenceValue(_reference_value) => { - unimplemented!("implement reference values!") - } - tucana::shared::node_value::Value::NodeFunctionId(id) => Executor::execute(self, *id), - } - } -} diff --git a/src/implementation/control.rs b/src/implementation/control.rs index 4b93305..4941ebe 100644 --- a/src/implementation/control.rs +++ b/src/implementation/control.rs @@ -31,7 +31,7 @@ fn r#if(values: &[Value], _ctx: &mut Context) -> Signal { let [ Value { kind: Some(Kind::StringValue(text)), - }, + } ] = values else { return Signal::Failure(RuntimeError::simple( @@ -51,9 +51,11 @@ fn r#if(values: &[Value], _ctx: &mut Context) -> Signal { }; if bool { - unimplemented!() + // Signal::Skip(vec![0]) } else { - unimplemented!() + // Signal::Return(Value { + // kind: Some(Kind::NullValue(0)), + // }) } } @@ -61,7 +63,7 @@ fn if_else(values: &[Value], _ctx: &mut Context) -> Signal { let [ Value { kind: Some(Kind::StringValue(text)), - }, + } ] = values else { return Signal::Failure(RuntimeError::simple( @@ -81,8 +83,8 @@ fn if_else(values: &[Value], _ctx: &mut Context) -> Signal { }; if bool { - unimplemented!() + // Signal::Skip(vec![1]) } else { - unimplemented!() + // Signal::Skip(vec![0]) } -} \ No newline at end of file +} diff --git a/src/implementation/number.rs b/src/implementation/number.rs index a5dcd67..b61f918 100644 --- a/src/implementation/number.rs +++ b/src/implementation/number.rs @@ -3,9 +3,9 @@ use std::f64; use tucana::shared::{Value, value::Kind}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use crate::{context::Context, error::RuntimeError}; -pub fn collect_number_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_number_functions() -> Vec<(&'static str, )> { vec![ ("std::number::add", add), ("std::number::multiply", multiply), From ea3ae268a3cbe41546e49be36df9b76aec49e6fd Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sun, 9 Nov 2025 12:25:09 +0100 Subject: [PATCH 03/10] ref: turned taurus into workspace --- Cargo.lock | 579 ++++++++++-------- Cargo.toml | 9 +- taurus/Cargo.toml | 18 + {src => taurus/src}/config/mod.rs | 0 {src => taurus/src}/context/argument.rs | 0 {src => taurus/src}/context/context.rs | 0 {src => taurus/src}/context/executor.rs | 0 {src => taurus/src}/context/mod.rs | 2 +- {src => taurus/src}/context/registry.rs | 2 +- {src => taurus/src}/context/signal.rs | 0 {src => taurus/src}/error/mod.rs | 0 {src => taurus/src}/implementation/array.rs | 3 +- {src => taurus/src}/implementation/boolean.rs | 0 {src => taurus/src}/implementation/control.rs | 0 {src => taurus/src}/implementation/http.rs | 0 {src => taurus/src}/implementation/mod.rs | 2 +- {src => taurus/src}/implementation/number.rs | 0 {src => taurus/src}/implementation/object.rs | 0 {src => taurus/src}/implementation/text.rs | 4 +- {src => taurus/src}/main.rs | 6 +- 20 files changed, 351 insertions(+), 274 deletions(-) create mode 100644 taurus/Cargo.toml rename {src => taurus/src}/config/mod.rs (100%) rename {src => taurus/src}/context/argument.rs (100%) rename {src => taurus/src}/context/context.rs (100%) rename {src => taurus/src}/context/executor.rs (100%) rename {src => taurus/src}/context/mod.rs (99%) rename {src => taurus/src}/context/registry.rs (99%) rename {src => taurus/src}/context/signal.rs (100%) rename {src => taurus/src}/error/mod.rs (100%) rename {src => taurus/src}/implementation/array.rs (99%) rename {src => taurus/src}/implementation/boolean.rs (100%) rename {src => taurus/src}/implementation/control.rs (100%) rename {src => taurus/src}/implementation/http.rs (100%) rename {src => taurus/src}/implementation/mod.rs (93%) rename {src => taurus/src}/implementation/number.rs (100%) rename {src => taurus/src}/implementation/object.rs (100%) rename {src => taurus/src}/implementation/text.rs (99%) rename {src => taurus/src}/main.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index 606c829..086b42a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -43,29 +43,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "async-nats" @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -123,15 +123,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ "axum-core", "bytes", @@ -145,8 +145,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "sync_wrapper", "tower", "tower-layer", @@ -155,9 +154,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.2" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", @@ -166,7 +165,6 @@ dependencies = [ "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -180,15 +178,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -210,18 +208,19 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.22" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" @@ -354,12 +353,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -419,9 +418,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -448,12 +447,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -468,6 +467,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -482,9 +487,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -551,9 +556,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -567,26 +572,26 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -603,9 +608,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -661,13 +666,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -675,6 +681,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -695,19 +702,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", "libc", "pin-project-lite", - "socket2 0.5.9", + "socket2", "tokio", "tower-service", "tracing", @@ -715,9 +723,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -728,9 +736,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -741,11 +749,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -756,42 +763,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -801,9 +804,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -822,9 +825,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -832,9 +835,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -853,22 +856,22 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -877,21 +880,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "log" @@ -907,9 +910,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -919,20 +922,20 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] name = "multimap" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "nkeys" @@ -981,9 +984,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl-probe" @@ -1045,9 +1048,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -1103,9 +1106,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -1118,9 +1121,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -1142,9 +1145,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.32" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -1152,9 +1155,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -1226,27 +1229,27 @@ dependencies = [ [[package]] name = "pulldown-cmark-to-cmark" -version = "21.0.0" +version = "21.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" +checksum = "8246feae3db61428fd0bb94285c690b460e4517d83152377543ca802357785f1" dependencies = [ "pulldown-cmark", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -1304,14 +1307,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -1321,9 +1324,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -1332,9 +1335,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "ring" @@ -1361,27 +1364,27 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki 0.103.8", "subtle", "zeroize", ] @@ -1410,9 +1413,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] @@ -1429,21 +1432,15 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] -[[package]] -name = "rustversion" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - [[package]] name = "ryu" version = "1.0.20" @@ -1452,11 +1449,11 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1474,9 +1471,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -1484,24 +1481,34 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1510,14 +1517,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -1581,37 +1589,24 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1626,9 +1621,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "subtle" @@ -1638,9 +1633,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -1684,15 +1679,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1717,9 +1712,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -1732,15 +1727,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -1748,9 +1743,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -1766,7 +1761,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2 0.6.0", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -1784,9 +1779,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -1806,9 +1801,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -1857,7 +1852,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.0", + "socket2", "sync_wrapper", "tokio", "tokio-stream", @@ -1869,9 +1864,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" +checksum = "4c40aaccc9f9eccf2cd82ebc111adc13030d23e887244bc9cfa5d1d636049de3" dependencies = [ "prettyplease", "proc-macro2", @@ -1894,9 +1889,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" +checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", "prost", @@ -1905,9 +1900,9 @@ dependencies = [ [[package]] name = "tonic-prost-build" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" +checksum = "b4a16cba4043dc3ff43fcb3f96b4c5c154c64cbd18ca8dce2ab2c6a451d058a2" dependencies = [ "prettyplease", "proc-macro2", @@ -1963,9 +1958,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -1974,9 +1969,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -2019,9 +2014,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicase" @@ -2031,9 +2026,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "untrusted" @@ -2043,13 +2038,14 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2081,17 +2077,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -2100,14 +2096,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -2124,16 +2120,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.5", ] [[package]] @@ -2151,14 +2147,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2167,42 +2180,84 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2210,27 +2265,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "windows_x86_64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -2238,9 +2295,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -2250,18 +2307,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -2291,15 +2348,15 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -2308,9 +2365,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -2319,9 +2376,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 85bd2b1..459e20c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,12 @@ -[package] -name = "taurus" +[workspace] +members = ["taurus"] +resolver = "3" + +[workspace.package] version = "0.1.0" edition = "2024" -[dependencies] +[workspace.dependencies] code0-flow = { version = "0.0.18" } tucana = { version = "0.0.39" } tokio = { version = "1.44.1", features = ["rt-multi-thread"] } diff --git a/taurus/Cargo.toml b/taurus/Cargo.toml new file mode 100644 index 0000000..7298d37 --- /dev/null +++ b/taurus/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "taurus" +version = "0.1.0" +edition = "2024" + +[dependencies] +code0-flow = { workspace = true } +tucana = { workspace = true } +tokio = { workspace = true } +log = { workspace = true } +futures-lite ={ workspace = true } +rand = { workspace = true } +base64 = { workspace = true } +env_logger = { workspace = true } +async-nats = { workspace = true } +prost = { workspace = true } +tonic-health = { workspace = true } +tonic = { workspace = true } diff --git a/src/config/mod.rs b/taurus/src/config/mod.rs similarity index 100% rename from src/config/mod.rs rename to taurus/src/config/mod.rs diff --git a/src/context/argument.rs b/taurus/src/context/argument.rs similarity index 100% rename from src/context/argument.rs rename to taurus/src/context/argument.rs diff --git a/src/context/context.rs b/taurus/src/context/context.rs similarity index 100% rename from src/context/context.rs rename to taurus/src/context/context.rs diff --git a/src/context/executor.rs b/taurus/src/context/executor.rs similarity index 100% rename from src/context/executor.rs rename to taurus/src/context/executor.rs diff --git a/src/context/mod.rs b/taurus/src/context/mod.rs similarity index 99% rename from src/context/mod.rs rename to taurus/src/context/mod.rs index 79a345c..fe00883 100644 --- a/src/context/mod.rs +++ b/taurus/src/context/mod.rs @@ -2,7 +2,7 @@ pub mod context; pub mod signal; pub mod argument; pub mod executor; -mod registry; +pub mod registry; use crate::error::RuntimeError; use std::{ diff --git a/src/context/registry.rs b/taurus/src/context/registry.rs similarity index 99% rename from src/context/registry.rs rename to taurus/src/context/registry.rs index e4fcc54..96cc416 100644 --- a/src/context/registry.rs +++ b/taurus/src/context/registry.rs @@ -48,4 +48,4 @@ impl FunctionStore { self.functions.insert(id.to_string(), func); } } -} +} \ No newline at end of file diff --git a/src/context/signal.rs b/taurus/src/context/signal.rs similarity index 100% rename from src/context/signal.rs rename to taurus/src/context/signal.rs diff --git a/src/error/mod.rs b/taurus/src/error/mod.rs similarity index 100% rename from src/error/mod.rs rename to taurus/src/error/mod.rs diff --git a/src/implementation/array.rs b/taurus/src/implementation/array.rs similarity index 99% rename from src/implementation/array.rs rename to taurus/src/implementation/array.rs index 58591a3..d1be4f4 100644 --- a/src/implementation/array.rs +++ b/taurus/src/implementation/array.rs @@ -3,7 +3,8 @@ use std::cmp::Ordering; use tucana::shared::{Value, value::Kind}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use crate::{context::Context, error::RuntimeError, }; +use crate::context::registry::HandlerFn; pub fn collect_array_functions() -> Vec<(&'static str, HandlerFn)> { vec![ diff --git a/src/implementation/boolean.rs b/taurus/src/implementation/boolean.rs similarity index 100% rename from src/implementation/boolean.rs rename to taurus/src/implementation/boolean.rs diff --git a/src/implementation/control.rs b/taurus/src/implementation/control.rs similarity index 100% rename from src/implementation/control.rs rename to taurus/src/implementation/control.rs diff --git a/src/implementation/http.rs b/taurus/src/implementation/http.rs similarity index 100% rename from src/implementation/http.rs rename to taurus/src/implementation/http.rs diff --git a/src/implementation/mod.rs b/taurus/src/implementation/mod.rs similarity index 93% rename from src/implementation/mod.rs rename to taurus/src/implementation/mod.rs index caa93b9..9067f60 100644 --- a/src/implementation/mod.rs +++ b/taurus/src/implementation/mod.rs @@ -1,4 +1,4 @@ -use crate::registry::HandlerFn; +use crate::context::registry::HandlerFn; mod array; mod boolean; diff --git a/src/implementation/number.rs b/taurus/src/implementation/number.rs similarity index 100% rename from src/implementation/number.rs rename to taurus/src/implementation/number.rs diff --git a/src/implementation/object.rs b/taurus/src/implementation/object.rs similarity index 100% rename from src/implementation/object.rs rename to taurus/src/implementation/object.rs diff --git a/src/implementation/text.rs b/taurus/src/implementation/text.rs similarity index 99% rename from src/implementation/text.rs rename to taurus/src/implementation/text.rs index 85f8854..7ba2f54 100644 --- a/src/implementation/text.rs +++ b/taurus/src/implementation/text.rs @@ -1,9 +1,9 @@ use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use crate::{context::Context, error::RuntimeError, }; use base64::Engine; use tucana::shared::{ListValue, Value, value::Kind}; -pub fn collect_text_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_text_functions() -> Vec<(&'static str, crate::reg)> { vec![ ("std::text::as_bytes", as_bytes), ("std::text::byte_size", byte_size), diff --git a/src/main.rs b/taurus/src/main.rs similarity index 97% rename from src/main.rs rename to taurus/src/main.rs index 1300e01..ff67944 100644 --- a/src/main.rs +++ b/taurus/src/main.rs @@ -1,24 +1,22 @@ mod config; pub mod context; pub mod error; -mod executor; pub mod implementation; -pub mod registry; use crate::config::Config; use crate::context::signal::Signal; -use crate::executor::Executor; use crate::implementation::collect; use code0_flow::flow_config::load_env_file; use context::Context; use futures_lite::StreamExt; use log::error; use prost::Message; -use registry::FunctionStore; use std::collections::HashMap; use tonic_health::pb::health_server::HealthServer; use tucana::shared::value::Kind; use tucana::shared::{ExecutionFlow, NodeFunction, Value}; +use crate::context::executor::Executor; +use crate::context::registry::FunctionStore; fn handle_message(flow: ExecutionFlow, store: &FunctionStore) -> Signal { let context = Context::new(); From 7050c894021122ac956b94935b3c529ab84f661f Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sun, 9 Nov 2025 19:20:10 +0100 Subject: [PATCH 04/10] feat: used new macro to match input arguments --- taurus/src/context/argument.rs | 77 + taurus/src/context/mod.rs | 1 + taurus/src/context/registry.rs | 50 +- taurus/src/implementation/array.rs | 1884 +++++++--------------- taurus/src/implementation/boolean.rs | 711 +++------ taurus/src/implementation/control.rs | 72 +- taurus/src/implementation/http.rs | 76 +- taurus/src/implementation/mod.rs | 4 +- taurus/src/implementation/number.rs | 1944 ++++------------------ taurus/src/implementation/object.rs | 685 ++------ taurus/src/implementation/text.rs | 2213 +++++--------------------- 11 files changed, 1775 insertions(+), 5942 deletions(-) diff --git a/taurus/src/context/argument.rs b/taurus/src/context/argument.rs index 98a248a..f6965c5 100644 --- a/taurus/src/context/argument.rs +++ b/taurus/src/context/argument.rs @@ -1,3 +1,9 @@ +use std::convert::Infallible; +use tucana::shared::{ListValue, Struct, Value}; +use tucana::shared::value::Kind; +use crate::context::signal::Signal; +use crate::error::RuntimeError; + #[derive(Clone, Debug)] pub enum Argument { // Eval => Evaluated Value @@ -12,4 +18,75 @@ pub enum Argument { pub enum ParameterNode { Eager, Lazy +} + +pub trait TryFromArgument: Sized { + fn try_from_argument(a: &Argument) -> Result; +} + +fn type_err(msg: &str) -> Signal { + Signal::Failure(RuntimeError::simple("InvalidArgumentRuntimeError", msg.to_string())) +} + +impl TryFromArgument for Value { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(v) => Ok(v.clone()), + _ => Err(type_err("Expected evaluated value but got lazy thunk")), + } + } +} + +impl TryFromArgument for f64 { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) => Ok(*n), + _ => Err(type_err("Expected number")), + } + } +} + +impl TryFromArgument for bool { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { kind: Some(Kind::BoolValue(b)) }) => Ok(*b), + _ => Err(type_err("Expected boolean")), + } + } +} + +impl TryFromArgument for String { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { kind: Some(Kind::StringValue(s)) }) => Ok(s.clone()), + _ => Err(type_err("Expected string")), + } + } +} + +impl TryFromArgument for Struct { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { kind: Some(Kind::StructValue(s)) }) => Ok(s.clone()), + _ => Err(type_err("Expected struct")), + } + } +} + +impl TryFromArgument for ListValue { + fn try_from_argument(a: &Argument) -> Result { + match a { + Argument::Eval(Value { kind: Some(Kind::ListValue(list)) }) => Ok(list.clone()), + _ => Err(Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected array (ListValue)", + ))), + } + } +} + +impl From for RuntimeError { + fn from(never: Infallible) -> Self { + match never {} + } } \ No newline at end of file diff --git a/taurus/src/context/mod.rs b/taurus/src/context/mod.rs index fe00883..a8a8a25 100644 --- a/taurus/src/context/mod.rs +++ b/taurus/src/context/mod.rs @@ -3,6 +3,7 @@ pub mod signal; pub mod argument; pub mod executor; pub mod registry; +pub mod macros; use crate::error::RuntimeError; use std::{ diff --git a/taurus/src/context/registry.rs b/taurus/src/context/registry.rs index 96cc416..8b1fc8a 100644 --- a/taurus/src/context/registry.rs +++ b/taurus/src/context/registry.rs @@ -7,11 +7,8 @@ use std::collections::HashMap; /// - For eager params, the executor will already convert them to Argument::Eval(Value). /// - For lazy params, the executor will pass Argument::Thunk(node_id). /// - If a handler wants to execute a lazy arg, it calls run(node_id). -pub type HandlerFn = fn( - args: &[Argument], - ctx: &mut Context, - run: &mut dyn FnMut(i64) -> Signal, -) -> Signal; +pub type HandlerFn = + fn(args: &[Argument], ctx: &mut Context, run: &mut dyn FnMut(i64) -> Signal) -> Signal; pub struct HandlerFunctionEntry { pub handler: HandlerFn, @@ -29,6 +26,47 @@ impl Default for FunctionStore { } } +pub trait IntoFunctionEntry { + fn into_function_entry(self, param: Vec) -> HandlerFunctionEntry; + fn eager(self, param_amount: i8) -> HandlerFunctionEntry; + fn lazy(self, param_amount: i8) -> HandlerFunctionEntry; +} + +impl IntoFunctionEntry for HandlerFn { + fn into_function_entry(self, param: Vec) -> HandlerFunctionEntry { + HandlerFunctionEntry { + handler: self, + param_modes: param, + } + } + + fn eager(self, param_amount: i8) -> HandlerFunctionEntry { + let mut params = vec![]; + + for _ in 0..param_amount { + params.push(ParameterNode::Eager) + } + + HandlerFunctionEntry { + handler: self, + param_modes: params, + } + } + + fn lazy(self, param_amount: i8) -> HandlerFunctionEntry { + let mut params = vec![]; + + for _ in 0..param_amount { + params.push(ParameterNode::Lazy) + } + + HandlerFunctionEntry { + handler: self, + param_modes: params, + } + } +} + impl FunctionStore { /// Create a new, empty store. pub fn new() -> Self { @@ -48,4 +86,4 @@ impl FunctionStore { self.functions.insert(id.to_string(), func); } } -} \ No newline at end of file +} diff --git a/taurus/src/implementation/array.rs b/taurus/src/implementation/array.rs index d1be4f4..c051d0e 100644 --- a/taurus/src/implementation/array.rs +++ b/taurus/src/implementation/array.rs @@ -1,287 +1,234 @@ use std::cmp::Ordering; -use tucana::shared::{Value, value::Kind}; +use tucana::shared::{value::Kind, ListValue, Value}; +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, }; -use crate::context::registry::HandlerFn; +use crate::{context::Context, error::RuntimeError}; -pub fn collect_array_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_array_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("std::array::at", at), - ("std::array::concat", concat), - ("std::array::filter", filter), - ("std::array::find", find), - ("std::array::find_last", find_last), - ("std::array::find_index", find_index), - ("std::array::first", first), - ("std::array::last", last), - ("std::array::for_each", for_each), - ("std::array::map", map), - ("std::array::push", push), - ("std::array::pop", pop), - ("std::array::remove", remove), - ("std::array::is_empty", is_empty), - ("std::array::size", size), - ("std::array::index_of", index_of), - ("std::array::to_unique", to_unique), - ("std::array::sort", sort), - ("std::array::sort_reverse", sort_reverse), - ("std::array::reverse", reverse), - ("std::array::flat", flat), - ("std::array::min", min), - ("std::array::max", max), - ("std::array::sum", sum), - ("std::array::join", join), + ("std::array::at", HandlerFn::eager(at, 2)), + ("std::array::concat", HandlerFn::eager(concat, 2)), + ("std::array::filter", HandlerFn::eager(filter, 2)), + ("std::array::find", HandlerFn::eager(find, 2)), + ("std::array::find_last", HandlerFn::eager(find_last, 2)), + ("std::array::find_index", HandlerFn::eager(find_index, 2)), + ("std::array::first", HandlerFn::eager(first, 1)), + ("std::array::last", HandlerFn::eager(last, 1)), + ("std::array::for_each", HandlerFn::eager(for_each, 0)), + ("std::array::map", HandlerFn::eager(map, 2)), + ("std::array::push", HandlerFn::eager(push, 2)), + ("std::array::pop", HandlerFn::eager(pop, 1)), + ("std::array::remove", HandlerFn::eager(remove, 2)), + ("std::array::is_empty", HandlerFn::eager(is_empty, 1)), + ("std::array::size", HandlerFn::eager(size, 1)), + ("std::array::index_of", HandlerFn::eager(index_of, 2)), + ("std::array::to_unique", HandlerFn::eager(to_unique, 1)), + ("std::array::sort", HandlerFn::eager(sort, 2)), + ("std::array::sort_reverse", HandlerFn::eager(sort_reverse, 2)), + ("std::array::reverse", HandlerFn::eager(reverse, 1)), + ("std::array::flat", HandlerFn::eager(flat, 1)), + ("std::array::min", HandlerFn::eager(min, 1)), + ("std::array::max", HandlerFn::eager(max, 1)), + ("std::array::sum", HandlerFn::eager(sum, 1)), + ("std::array::join", HandlerFn::eager(join, 2)), ] } -fn at(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::NumberValue(index)), - }, - ] = values - else { +fn at(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // array, index + args!(args => array: ListValue, index: f64); + + if index < 0.0 { + return Signal::Failure(RuntimeError::simple_str( + "IndexOutOfBoundsRuntimeError", + "Negative index", + )); + } + let i = index as usize; + match array.values.get(i) { + Some(item) => Signal::Success(item.clone()), + None => Signal::Failure(RuntimeError::simple( + "IndexOutOfBoundsRuntimeError", + format!("Index {} out of bounds (len={})", i, array.values.len()), + )), + } +} + +fn concat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs_v: Value, rhs_v: Value); + + let Kind::ListValue(lhs) = lhs_v.kind.clone().ok_or_else(|| ()).unwrap_or_else(|_| Kind::NullValue(0)) else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!( - "Expected a list and a number as arguments but received {:?}", - values - ), + format!("Expected two arrays as arguments but received lhs={:?}", lhs_v), )); }; - - let item = array.values[*index as usize].clone(); - - Signal::Success(item) -} - -fn concat(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(lhs)), - }, - Value { - kind: Some(Kind::ListValue(rhs)), - }, - ] = values - else { + let Kind::ListValue(rhs) = rhs_v.kind.clone().ok_or_else(|| ()).unwrap_or_else(|_| Kind::NullValue(0)) else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), + format!("Expected two arrays as arguments but received rhs={:?}", rhs_v), )); }; let mut result = lhs.values.clone(); - result.extend(rhs.values.clone()); + result.extend(rhs.values.iter().cloned()); Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: result, - })), + kind: Some(Kind::ListValue(ListValue { values: result })), }) } -fn filter(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { +fn filter(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array".to_string(), + )); + }; + let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), + "Expected second argument to be an array of booleans".to_string(), )); }; - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); } - }); + } - let mut index = 0; + let mut i = 0usize; let new_array = array .values - .clone() - .into_iter() + .iter() .filter(|_| { - let predicate = predicate_collector[index]; - index += 1; - predicate + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep }) + .cloned() .collect::>(); Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), + kind: Some(Kind::ListValue(ListValue { values: new_array })), }) } -fn find(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); +fn find(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + }; + let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans")); }; - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); } - }); + } - let mut index = 0; - let item = array.values.clone().into_iter().find(|_| { - let predicate = predicate_collector[index]; - index += 1; - predicate + let mut i = 0usize; + let item = array.values.iter().cloned().find(|_| { + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep }); match item { - Some(item) => Signal::Success(item), - None => Signal::Failure(RuntimeError::simple( + Some(v) => Signal::Success(v), + None => Signal::Failure(RuntimeError::simple_str( "NotFoundError", - "No item found that satisfies the predicate".to_string(), + "No item found that satisfies the predicate", )), } } -fn find_last(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); +fn find_last(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + }; + let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans")); }; - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); } - }); + } - let mut index = 0; - let mut new_array = array.values.clone(); - new_array.reverse(); + let mut i = 0usize; + let mut reversed = array.values.clone(); + reversed.reverse(); - let item = new_array.into_iter().find(|_| { - let predicate = predicate_collector[index]; - index += 1; - predicate + let item = reversed.into_iter().find(|_| { + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep }); match item { - Some(item) => Signal::Success(item), - None => Signal::Failure(RuntimeError::simple( + Some(v) => Signal::Success(v), + None => Signal::Failure(RuntimeError::simple_str( "NotFoundError", - "No item found that satisfies the predicate".to_string(), + "No item found that satisfies the predicate", )), } } -fn find_index(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_predicate)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); +fn find_index(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, predicate_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + }; + let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans")); }; - let mut predicate_collector = vec![]; - resolved_predicate.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::BoolValue(pred)), - } = v.clone() - { - predicate_collector.push(pred); + let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); + for v in &resolved_predicate.values { + if let Some(Kind::BoolValue(b)) = v.kind { + preds.push(b); } - }); - - let mut index = 0; - let new_array = array.values.clone(); - - let item = new_array.into_iter().find(|_| { - let predicate = predicate_collector[index]; + } - if !predicate { - index += 1; + let mut idx = 0usize; + let found = array.values.iter().find(|_| { + let keep = *preds.get(idx).unwrap_or(&false); + if !keep { + idx += 1; } - predicate + keep }); - match item { + match found { Some(_) => Signal::Success(Value { - kind: Some(Kind::NumberValue(index as f64)), + kind: Some(Kind::NumberValue(idx as f64)), }), - None => Signal::Failure(RuntimeError::simple( + None => Signal::Failure(RuntimeError::simple_str( "NotFoundError", - "No item found that satisfies the predicate".to_string(), + "No item found that satisfies the predicate", )), } } -fn first(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; +fn first(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); match array.values.first() { - Some(item) => Signal::Success(item.clone()), + Some(v) => Signal::Success(v.clone()), None => Signal::Failure(RuntimeError::simple_str( "ArrayEmptyRuntimeError", "This array is empty", @@ -289,21 +236,10 @@ fn first(values: &[Value], _ctx: &mut Context) -> Signal { } } -fn last(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), - )); - }; - +fn last(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); match array.values.last() { - Some(item) => Signal::Success(item.clone()), + Some(v) => Signal::Success(v.clone()), None => Signal::Failure(RuntimeError::simple_str( "ArrayEmptyRuntimeError", "This array is empty", @@ -311,1367 +247,637 @@ fn last(values: &[Value], _ctx: &mut Context) -> Signal { } } -/* - * for_each has no implementation - * - * Reason: - * The definition itself takes in an array and a node - * The node itself will be executed on the arrays elements - * If the node is (CONSUMER) resolved it goes in this function --> therefor all code is already executed - */ -fn for_each(_values: &[Value], _ctx: &mut Context) -> Signal { +/// for_each has no implementation +/// +/// Reason: +/// The definition itself takes in an array and a node +/// The node itself will be executed on the arrays elements +/// If the node is (CONSUMER) resolved it goes in this function --> therefor all code is already executed +fn for_each(_args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // Already executed by the engine (consumer); return Null Signal::Success(Value { kind: Some(Kind::NullValue(0)), }) } -/* - * Same as for_each - * - * Reason: - * The definition itself takes in an array and a node - * The node itself will be executed on the arrays elements - * If the node is (TRANSFORM) resolved it goes in this function --> therefor all code is already executed - * The TRANSFORM node is gives a new item as a result - */ -fn map(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(_array)), - }, - Value { - kind: Some(Kind::ListValue(transform_result)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn map(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // (array, transformed_results[]) + args!(args => _array_v: Value, transform_v: Value); + let Kind::ListValue(transform_result) = transform_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), + "Expected transform result to be an array", )); }; - Signal::Success(Value { kind: Some(Kind::ListValue(transform_result.clone())), }) } -fn push(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - item, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn push(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, item: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected first argument to be an array", )); }; - - let mut new_array = array.clone(); - new_array.values.push(item.clone()); + array.values.push(item); Signal::Success(Value { - kind: Some(Kind::ListValue(new_array)), + kind: Some(Kind::ListValue(array)), }) } -fn pop(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn pop(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected an array as an argument", )); }; - - let mut new_array = array.clone(); - new_array.values.pop(); + array.values.pop(); Signal::Success(Value { - kind: Some(Kind::ListValue(new_array)), + kind: Some(Kind::ListValue(array)), }) } -fn remove(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - item, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn remove(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, item: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected first argument to be an array", )); }; - let mut new_array = array.clone(); - let item_clone = item.clone(); - let index = match new_array.values.iter().position(|x| *x == item_clone) { - Some(index) => index, - None => { - return Signal::Failure(RuntimeError::simple( - "ValueNotFoundRuntimeError", - format!("Item {:?} not found in array", item_clone), - )); - } - }; - - new_array.values.remove(index); - - Signal::Success(Value { - kind: Some(Kind::ListValue(new_array)), - }) + if let Some(index) = array.values.iter().position(|x| *x == item) { + array.values.remove(index); + Signal::Success(Value { + kind: Some(Kind::ListValue(array)), + }) + } else { + Signal::Failure(RuntimeError::simple( + "ValueNotFoundRuntimeError", + format!("Item {:?} not found in array", item), + )) + } } -fn is_empty(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn is_empty(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected an array as an argument", )); }; - Signal::Success(Value { kind: Some(Kind::BoolValue(array.values.is_empty())), }) } -fn size(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn size(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected an array as an argument", )); }; - Signal::Success(Value { kind: Some(Kind::NumberValue(array.values.len() as f64)), }) } -fn index_of(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - item, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn index_of(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, item: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), + "Expected first argument to be an array", )); }; - let new_array = array.clone(); - let item_clone = item.clone(); - let index = match new_array.values.iter().position(|x| *x == item_clone) { - Some(index) => index, - None => { - return Signal::Failure(RuntimeError::simple( - "ValueNotFoundRuntimeError", - format!("Item {:?} not found in array", item_clone), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(index as f64)), - }) + match array.values.iter().position(|x| *x == item) { + Some(i) => Signal::Success(Value { + kind: Some(Kind::NumberValue(i as f64)), + }), + None => Signal::Failure(RuntimeError::simple( + "ValueNotFoundRuntimeError", + format!("Item {:?} not found in array", item), + )), + } } -fn to_unique(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn to_unique(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected an array as an argument", )); }; - let mut unique_values = Vec::new(); - - for value in &array.values { - if !unique_values.contains(value) { - unique_values.push(value.clone()); + let mut unique = Vec::::new(); + for v in &array.values { + if !unique.contains(v) { + unique.push(v.clone()); } } Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: unique_values, - })), + kind: Some(Kind::ListValue(ListValue { values: unique })), }) } -fn sort(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_comparator)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); +fn sort(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + // array, resolved comparator yields -1/0/1 sequence + args!(args => array_v: Value, cmp_v: Value); + let Kind::ListValue(mut arr) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + }; + let Kind::ListValue(cmp_vals) = cmp_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of numbers")); }; - let mut comparate_collector = vec![]; - resolved_comparator.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::NumberValue(comp)), - } = v.clone() - { - comparate_collector.push(comp); + let mut comps: Vec = Vec::new(); + for v in &cmp_vals.values { + if let Some(Kind::NumberValue(n)) = v.kind { + comps.push(n); } - }); + } - let mut index = 0; - let mut new_array = array.values.clone(); - new_array.sort_by(|_, _| { - let comp = comparate_collector[index]; - index += 1; + let mut i = 0usize; + arr.values.sort_by(|_, _| { + let comp = *comps.get(i).unwrap_or(&0.0); + i += 1; match comp { - -1.0 => Ordering::Less, + n if n < 0.0 => Ordering::Less, 0.0 => Ordering::Equal, _ => Ordering::Greater, } }); Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), + kind: Some(Kind::ListValue(arr)), }) } -fn sort_reverse(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::ListValue(resolved_comparator)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received {:?}", values), - )); +fn sort_reverse(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value, cmp_v: Value); + let Kind::ListValue(mut arr) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + }; + let Kind::ListValue(cmp_vals) = cmp_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of numbers")); }; - let mut comparate_collector = vec![]; - resolved_comparator.values.iter().for_each(|v| { - if let Value { - kind: Some(Kind::NumberValue(comp)), - } = v.clone() - { - comparate_collector.push(comp); + let mut comps: Vec = Vec::new(); + for v in &cmp_vals.values { + if let Some(Kind::NumberValue(n)) = v.kind { + comps.push(n); } - }); + } - let mut index = 0; - let mut new_array = array.values.clone(); - new_array.reverse(); + arr.values.reverse(); // keep behavior consistent with original - new_array.sort_by(|_, _| { - let comp = comparate_collector[index]; - index += 1; + let mut i = 0usize; + arr.values.sort_by(|_, _| { + let comp = *comps.get(i).unwrap_or(&0.0); + i += 1; match comp { - -1.0 => Ordering::Less, + n if n < 0.0 => Ordering::Less, 0.0 => Ordering::Equal, _ => Ordering::Greater, } }); Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), + kind: Some(Kind::ListValue(arr)), }) } -fn reverse(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn reverse(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected an array as an argument", )); }; - - let mut new_array = array.values.clone(); - new_array.reverse(); - + array.values.reverse(); Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: new_array, - })), + kind: Some(Kind::ListValue(array)), }) } -fn flat(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( +fn flat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array_v: Value); + let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", - format!("Expected an array as an argument but received {:?}", values), + "Expected an array as an argument", )); }; - let mut flattable_array: Vec> = Vec::new(); - + let mut out: Vec = Vec::new(); for item in &array.values { - if let Value { - kind: Some(Kind::ListValue(sub_array)), - } = item - { - flattable_array.push(sub_array.values.clone()); - } else { - flattable_array.push(vec![item.clone()]); + match &item.kind { + Some(Kind::ListValue(sub)) => out.extend(sub.values.iter().cloned()), + _ => out.push(item.clone()), } } - let flattend = flattable_array - .into_iter() - .flatten() - .collect::>(); - Signal::Success(Value { - kind: Some(Kind::ListValue(tucana::shared::ListValue { - values: flattend, - })), + kind: Some(Kind::ListValue(ListValue { values: out })), }) } -fn min(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an array of numbers as an argument but received {:?}", - values - ), - )); - }; +fn min(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); - let mut new_array = vec![]; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = a - { - new_array.push(number); + let mut nums: Vec = Vec::new(); + for v in &array.values { + if let Some(Kind::NumberValue(n)) = v.kind { + nums.push(n); } - }); - - let min = new_array.iter().min_by(|a, b| a.total_cmp(b)); + } - match min { - Some(min) => Signal::Success(Value { - kind: Some(Kind::NumberValue(*min)), + match nums.iter().min_by(|a, b| a.total_cmp(b)) { + Some(m) => Signal::Success(Value { + kind: Some(Kind::NumberValue(*m)), }), - None => Signal::Failure(RuntimeError::simple( - "ArrayEmptyRuntimeError", - "Array is empty".to_string(), - )), + None => Signal::Failure(RuntimeError::simple_str("ArrayEmptyRuntimeError", "Array is empty")), } } -fn max(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an array of numbers as an argument but received {:?}", - values - ), - )); - }; +fn max(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); - let mut new_array = vec![]; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = a - { - new_array.push(number); + let mut nums: Vec = Vec::new(); + for v in &array.values { + if let Some(Kind::NumberValue(n)) = v.kind { + nums.push(n); } - }); - - let max = new_array.iter().max_by(|a, b| a.total_cmp(b)); + } - match max { - Some(max) => Signal::Success(Value { - kind: Some(Kind::NumberValue(*max)), + match nums.iter().max_by(|a, b| a.total_cmp(b)) { + Some(m) => Signal::Success(Value { + kind: Some(Kind::NumberValue(*m)), }), - None => Signal::Failure(RuntimeError::simple( - "ArrayEmptyRuntimeError", - "Array is empty".to_string(), - )), + None => Signal::Failure(RuntimeError::simple_str("ArrayEmptyRuntimeError", "Array is empty")), } } -fn sum(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an array of numbers as an argument but received {:?}", - values - ), - )); - }; +fn sum(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue); - let mut sum = 0.0; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = a - { - sum += number; + let mut s = 0.0; + for v in &array.values { + if let Some(Kind::NumberValue(n)) = v.kind { + s += n; } - }); + } Signal::Success(Value { - kind: Some(Kind::NumberValue(sum)), + kind: Some(Kind::NumberValue(s)), }) } -fn join(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(array)), - }, - Value { - kind: Some(Kind::StringValue(separator)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected array of text and a text as arguments but received {:?}", - values - ), - )); - }; +fn join(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => array: ListValue, separator: String); - let mut collector = vec![]; - array.values.clone().into_iter().for_each(|a| { - if let Value { - kind: Some(Kind::StringValue(string)), - } = a - { - collector.push(string); + let mut parts: Vec = Vec::new(); + for v in &array.values { + if let Some(Kind::StringValue(s)) = &v.kind { + parts.push(s.clone()); } - }); - - let joined = collector.join(separator); + } Signal::Success(Value { - kind: Some(Kind::StringValue(joined)), + kind: Some(Kind::StringValue(parts.join(&separator))), }) } - #[cfg(test)] mod tests { use super::*; use crate::context::Context; - use tucana::shared::{ListValue, Value, value::Kind}; + use tucana::shared::{value::Kind, ListValue, Value}; - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), + // --- helpers ------------------------------------------------------------- + fn a_val(v: Value) -> Argument { Argument::Eval(v) } + fn v_num(n: f64) -> Value { Value { kind: Some(Kind::NumberValue(n)) } } + fn v_str(s: &str) -> Value { Value { kind: Some(Kind::StringValue(s.to_string())) } } + fn v_bool(b: bool) -> Value { Value { kind: Some(Kind::BoolValue(b)) } } + fn v_list(values: Vec) -> Value { Value { kind: Some(Kind::ListValue(ListValue { values })) } } + + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + x => panic!("Expected NumberValue, got {:?}", x), } } - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + x => panic!("Expected StringValue, got {:?}", x), } } - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), + fn expect_list(sig: Signal) -> Vec { + match sig { + Signal::Success(Value { kind: Some(Kind::ListValue(ListValue { values })) }) => values, + x => panic!("Expected ListValue, got {:?}", x), } } - - // Helper function to create an array value - fn create_array_value(values: Vec) -> Value { - Value { - kind: Some(Kind::ListValue(ListValue { values })), + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + x => panic!("Expected BoolValue, got {:?}", x), } } + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) + } + + // --- at ------------------------------------------------------------------ #[test] fn test_at_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(10.0), - create_number_value(20.0), - create_number_value(30.0), - ]); - - // Test getting first element - let values = vec![array.clone(), create_number_value(0.0)]; - let result = at(&values, &mut ctx); - match result { - Signal::Success(v) => match v.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), - _ => panic!("Expected NumberValue"), - }, - _ => panic!("Expected NumberValue"), - } - - // Test getting second element - let values = vec![array.clone(), create_number_value(1.0)]; - let signal = at(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 20.0), - _ => panic!("Expected NumberValue"), - } + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_num(10.0), v_num(20.0), v_num(30.0)]); - // Test getting third element - let values = vec![array, create_number_value(2.0)]; - let signal = at(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 30.0), - _ => panic!("Expected NumberValue"), - } + assert_eq!(expect_num(at(&[a_val(arr.clone()), a_val(v_num(0.0))], &mut ctx, &mut run)), 10.0); + assert_eq!(expect_num(at(&[a_val(arr.clone()), a_val(v_num(1.0))], &mut ctx, &mut run)), 20.0); + assert_eq!(expect_num(at(&[a_val(arr), a_val(v_num(2.0))], &mut ctx, &mut run)), 30.0); } #[test] fn test_at_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(0.0)]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for second parameter - let values = vec![array, create_string_value("not_number")]; - let result = at(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_num(1.0)]); + + // wrong arg count + match at(&[a_val(arr.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + // wrong type first arg + match at(&[a_val(v_str("not_array")), a_val(v_num(0.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + // wrong type second arg + match at(&[a_val(arr.clone()), a_val(v_str("x"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + // oob / negative + match at(&[a_val(arr.clone()), a_val(v_num(9.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match at(&[a_val(arr), a_val(v_num(-1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- concat -------------------------------------------------------------- #[test] fn test_concat_success() { - let mut ctx = Context::new(); - - let array1 = create_array_value(vec![create_number_value(1.0), create_number_value(2.0)]); - let array2 = create_array_value(vec![create_number_value(3.0), create_number_value(4.0)]); - - let values = vec![array1, array2]; - let signal = concat(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 4); - - // Check each element - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 2.0), - _ => panic!("Expected NumberValue at index 1"), - } - match &list.values[2].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at index 2"), - } - match &list.values[3].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 4.0), - _ => panic!("Expected NumberValue at index 3"), - } - } - _ => panic!("Expected ListValue"), - } + let mut ctx = Context::new(); let mut run = dummy_run; + let a = v_list(vec![v_num(1.0), v_num(2.0)]); + let b = v_list(vec![v_num(3.0), v_num(4.0)]); + let out = expect_list(concat(&[a_val(a), a_val(b)], &mut ctx, &mut run)); + assert_eq!(out.len(), 4); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[3].kind, Some(Kind::NumberValue(4.0))); } #[test] fn test_concat_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = concat(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), array.clone()]; - let result = concat(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type for second parameter - let values = vec![array, create_number_value(42.0)]; - let result = concat(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_num(1.0)]); + match concat(&[a_val(arr.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match concat(&[a_val(v_str("not_array")), a_val(arr.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match concat(&[a_val(arr), a_val(v_num(42.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- filter / find / find_last / find_index ------------------------------ #[test] fn test_filter_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - let predicate = create_array_value(vec![ - create_bool_value(true), - create_bool_value(false), - create_bool_value(true), - ]); - - let values = vec![array, predicate]; - let signal = filter(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 2); - - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at index 1"), - } - } - _ => panic!("Expected ListValue"), - } + let mut ctx = Context::new(); let mut run = dummy_run; + let array = v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]); + let predicate = v_list(vec![v_bool(true), v_bool(false), v_bool(true)]); + let out = expect_list(filter(&[a_val(array), a_val(predicate)], &mut ctx, &mut run)); + assert_eq!(out.len(), 2); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[1].kind, Some(Kind::NumberValue(3.0))); } #[test] fn test_filter_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - let predicate = create_array_value(vec![create_bool_value(true)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = filter(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), predicate.clone()]; - let result = filter(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for second parameter - let values = vec![array, create_number_value(42.0)]; - let result = filter(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut ctx = Context::new(); let mut run = dummy_run; + let array = v_list(vec![v_num(1.0)]); + let predicate = v_list(vec![v_bool(true)]); + match filter(&[a_val(array.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match filter(&[a_val(v_str("not_array")), a_val(predicate.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match filter(&[a_val(array), a_val(v_num(1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- first / last -------------------------------------------------------- #[test] fn test_first_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("first"), - create_string_value("second"), - create_string_value("third"), - ]); - - let values = vec![array]; - let signal = first(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "first"), - _ => panic!("Expected StringValue"), - } + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); + assert_eq!(expect_str(first(&[a_val(arr)], &mut ctx, &mut run)), "first"); } #[test] fn test_first_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = first(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = first(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = first(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut ctx = Context::new(); let mut run = dummy_run; + match first(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match first(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match first(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } #[test] fn test_last_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("first"), - create_string_value("second"), - create_string_value("last"), - ]); - - let values = vec![array]; - let signal = last(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "last"), - _ => panic!("Expected StringValue"), - } + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_str("first"), v_str("second"), v_str("last")]); + assert_eq!(expect_str(last(&[a_val(arr)], &mut ctx, &mut run)), "last"); } #[test] fn test_last_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = last(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = last(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_push_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![create_number_value(1.0), create_number_value(2.0)]); - let new_element = create_number_value(3.0); - - let values = vec![array, new_element]; - let signal = push(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 3); - - match &list.values[2].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at last index"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_push_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = push(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(42.0)]; - let result = push(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_pop_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - - let values = vec![array]; - let signal = pop(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 2); - - // Check that the last element was removed - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 2.0), - _ => panic!("Expected NumberValue at index 1"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_pop_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = pop(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = pop(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_empty_success() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let signal = is_empty(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test with non-empty array - let non_empty_array = create_array_value(vec![create_number_value(1.0)]); - let values = vec![non_empty_array]; - let signal = is_empty(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } - - #[test] - fn test_is_empty_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = is_empty(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = is_empty(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_size_success() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } - - // Test with array of 3 elements - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - let values = vec![array]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), - _ => panic!("Expected NumberValue"), - } - } - - #[test] - fn test_size_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut ctx = Context::new(); let mut run = dummy_run; + match last(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match last(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- for_each / map ------------------------------------------------------ #[test] - fn test_reverse_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.0), - create_number_value(2.0), - create_number_value(3.0), - ]); - - let values = vec![array]; - let signal = reverse(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 3); - - // Check that elements are reversed - match &list.values[0].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 3.0), - _ => panic!("Expected NumberValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 2.0), - _ => panic!("Expected NumberValue at index 1"), - } - match &list.values[2].kind { - Some(Kind::NumberValue(val)) => assert_eq!(*val, 1.0), - _ => panic!("Expected NumberValue at index 2"), - } - } - _ => panic!("Expected ListValue"), + fn test_for_each_and_map() { + let mut ctx = Context::new(); let mut run = dummy_run; + match for_each(&[], &mut ctx, &mut run) { + Signal::Success(Value { kind: Some(Kind::NullValue(_)) }) => {}, + x => panic!("expected NullValue, got {:?}", x), } + let transformed = v_list(vec![v_str("X"), v_str("Y")]); + let out = expect_list(map(&[a_val(v_list(vec![v_num(1.0), v_num(2.0)])), a_val(transformed.clone())], &mut ctx, &mut run)); + let expected = match transformed.kind { Some(Kind::ListValue(ListValue { values })) => values, _ => unreachable!() }; + assert_eq!(out, expected); } + // --- push / pop / remove ------------------------------------------------- #[test] - fn test_reverse_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = reverse(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = reverse(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_sum_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(1.5), - create_number_value(2.5), - create_number_value(3.0), - ]); - - let values = vec![array]; - let signal = sum(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 7.0), - _ => panic!("Expected NumberValue"), - } + fn test_push_success() { + let mut ctx = Context::new(); let mut run = dummy_run; + let out = expect_list(push(&[a_val(v_list(vec![v_num(1.0), v_num(2.0)])), a_val(v_num(3.0))], &mut ctx, &mut run)); + assert_eq!(out.len(), 3); + assert_eq!(out[2].kind, Some(Kind::NumberValue(3.0))); } #[test] - fn test_sum_empty_array() { - let mut ctx = Context::new(); - - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let signal = sum(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } + fn test_push_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + match push(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match push(&[a_val(v_str("nope")), a_val(v_num(1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } #[test] - fn test_sum_error() { - let mut ctx = Context::new(); - - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = sum(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong number of parameters - let values = vec![]; - let result = sum(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + fn test_pop_success() { + let mut ctx = Context::new(); let mut run = dummy_run; + let out = expect_list(pop(&[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], &mut ctx, &mut run)); + assert_eq!(out.len(), 2); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[1].kind, Some(Kind::NumberValue(2.0))); } #[test] - fn test_join_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("hello"), - create_string_value("world"), - create_string_value("test"), - ]); - let separator = create_string_value(", "); - - let values = vec![array, separator]; - let signal = join(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "hello, world, test"), - _ => panic!("Expected StringValue"), - } + fn test_pop_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + match pop(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match pop(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } #[test] - fn test_join_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_string_value("hello")]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = join(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_string_value(",")]; - let result = join(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for second parameter - let values = vec![array, create_number_value(42.0)]; - let result = join(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + fn test_remove_success_and_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + // success + let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); + let out = expect_list(remove(&[a_val(arr), a_val(v_str("second"))], &mut ctx, &mut run)); + assert_eq!(out.len(), 2); + assert_eq!(out[0].kind, Some(Kind::StringValue("first".into()))); + assert_eq!(out[1].kind, Some(Kind::StringValue("third".into()))); + // errors + match remove(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match remove(&[a_val(v_str("nope")), a_val(v_num(0.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match remove(&[a_val(v_list(vec![v_num(1.0)])), a_val(v_num(999.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- is_empty / size ----------------------------------------------------- #[test] - fn test_min_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(5.0), - create_number_value(1.0), - create_number_value(3.0), - create_number_value(2.0), - ]); - - let values = vec![array]; - let signal = min(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), - } + fn test_is_empty_and_size() { + let mut ctx = Context::new(); let mut run = dummy_run; + assert!(expect_bool(is_empty(&[a_val(v_list(vec![]))], &mut ctx, &mut run))); + assert!(!expect_bool(is_empty(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run))); + assert_eq!(expect_num(size(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), 0.0); + assert_eq!(expect_num(size(&[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], &mut ctx, &mut run)), 3.0); } #[test] - fn test_min_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = min(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = min(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + fn test_is_empty_error_and_size_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + match is_empty(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match is_empty(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match size(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match size(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- index_of / to_unique ------------------------------------------------ #[test] - fn test_max_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(5.0), - create_number_value(1.0), - create_number_value(8.0), - create_number_value(2.0), - ]); + fn test_index_of_and_to_unique() { + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_num(10.0), v_num(42.0), v_num(30.0), v_num(42.0)]); + assert_eq!(expect_num(index_of(&[a_val(arr.clone()), a_val(v_num(42.0))], &mut ctx, &mut run)), 1.0); + match index_of(&[a_val(arr.clone()), a_val(v_num(999.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - let values = vec![array]; - let signal = max(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), - _ => panic!("Expected NumberValue"), - } + let uniq = expect_list(to_unique(&[a_val(arr)], &mut ctx, &mut run)); + assert_eq!(uniq.len(), 3); + assert_eq!(uniq[0].kind, Some(Kind::NumberValue(10.0))); + assert_eq!(uniq[1].kind, Some(Kind::NumberValue(42.0))); + assert_eq!(uniq[2].kind, Some(Kind::NumberValue(30.0))); } #[test] - fn test_max_error() { - let mut ctx = Context::new(); - - // Test with empty array - let empty_array = create_array_value(vec![]); - let values = vec![empty_array]; - let result = max(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong parameter type - let values = vec![create_string_value("not_array")]; - let result = max(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + fn test_index_of_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + match index_of(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match index_of(&[a_val(v_str("nope")), a_val(v_num(1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } + // --- sort / sort_reverse ------------------------------------------------- #[test] - fn test_index_of_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_number_value(10.0), - create_number_value(42.0), - create_number_value(30.0), - create_number_value(42.0), // duplicate - ]); - let search_element = create_number_value(42.0); + fn test_sort_and_sort_reverse() { + let mut ctx = Context::new(); let mut run = dummy_run; - let values = vec![array, search_element]; - let signal = index_of(&values, &mut ctx); + // We don't rely on actual values; ordering is driven by the comparator sequence. + let arr = v_list(vec![v_str("a"), v_str("b"), v_str("c"), v_str("d")]); + let comps = v_list(vec![v_num(-1.0), v_num(1.0), v_num(0.0), v_num(-1.0)]); + let out = expect_list(sort(&[a_val(arr.clone()), a_val(comps.clone())], &mut ctx, &mut run)); + assert_eq!(out.len(), 4); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), // Should return first occurrence - _ => panic!("Expected NumberValue"), - } + let out_r = expect_list(sort_reverse(&[a_val(arr), a_val(comps)], &mut ctx, &mut run)); + assert_eq!(out_r.len(), 4); } + // --- reverse / flat ------------------------------------------------------ #[test] - fn test_index_of_not_found() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("hello"), - create_string_value("world"), - ]); - let search_element = create_string_value("missing"); + fn test_reverse_success_and_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + let out = expect_list(reverse(&[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], &mut ctx, &mut run)); + assert_eq!(out[0].kind, Some(Kind::NumberValue(3.0))); + assert_eq!(out[2].kind, Some(Kind::NumberValue(1.0))); - let values = vec![array, search_element]; - let result = index_of(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + match reverse(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match reverse(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } #[test] - fn test_index_of_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = index_of(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(42.0)]; - let result = index_of(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_remove_success() { - let mut ctx = Context::new(); - - let array = create_array_value(vec![ - create_string_value("first"), - create_string_value("second"), - create_string_value("third"), + fn test_flat_success() { + let mut ctx = Context::new(); let mut run = dummy_run; + let nested = v_list(vec![ + v_num(1.0), + v_list(vec![v_num(2.0), v_num(3.0)]), + v_list(vec![]), + v_num(4.0), ]); - let item_to_remove = create_string_value("second"); // Remove middle element - - let values = vec![array, item_to_remove]; - let signal = remove(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 2); - - match &list.values[0].kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "first"), - _ => panic!("Expected StringValue at index 0"), - } - match &list.values[1].kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "third"), - _ => panic!("Expected StringValue at index 1"), - } - } - _ => panic!("Expected ListValue"), - } - } - - #[test] - fn test_remove_error() { - let mut ctx = Context::new(); - let array = create_array_value(vec![create_number_value(1.0)]); - - // Test with wrong number of parameters - let values = vec![array.clone()]; - let result = remove(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with wrong type for first parameter - let values = vec![create_string_value("not_array"), create_number_value(0.0)]; - let result = remove(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - // Test with item not found - let values = vec![array, create_number_value(999.0)]; - let result = remove(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let out = expect_list(flat(&[a_val(nested)], &mut ctx, &mut run)); + assert_eq!(out.len(), 4); + assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); + assert_eq!(out[1].kind, Some(Kind::NumberValue(2.0))); + assert_eq!(out[2].kind, Some(Kind::NumberValue(3.0))); + assert_eq!(out[3].kind, Some(Kind::NumberValue(4.0))); + } + + // --- min / max / sum ----------------------------------------------------- + #[test] + fn test_min_max_sum_success_and_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + let nums = v_list(vec![v_num(5.0), v_num(1.0), v_num(8.0), v_num(2.0)]); + assert_eq!(expect_num(min(&[a_val(nums.clone())], &mut ctx, &mut run)), 1.0); + assert_eq!(expect_num(max(&[a_val(nums.clone())], &mut ctx, &mut run)), 8.0); + assert_eq!(expect_num(sum(&[a_val(nums)], &mut ctx, &mut run)), 16.0); + + // empty + match min(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match max(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + assert_eq!(expect_num(sum(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), 0.0); + + // wrong type + match sum(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match min(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match max(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + } + + // --- join ---------------------------------------------------------------- + #[test] + fn test_join_success_and_error() { + let mut ctx = Context::new(); let mut run = dummy_run; + let arr = v_list(vec![v_str("hello"), v_str("world"), v_str("test")]); + assert_eq!(expect_str(join(&[a_val(arr), a_val(v_str(", "))], &mut ctx, &mut run)), "hello, world, test"); + + // errors + let arr2 = v_list(vec![v_str("hello")]); + match join(&[a_val(arr2.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match join(&[a_val(v_str("not_array")), a_val(v_str(","))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match join(&[a_val(arr2), a_val(v_num(42.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } } } diff --git a/taurus/src/implementation/boolean.rs b/taurus/src/implementation/boolean.rs index 4adf8c5..ae7fd92 100644 --- a/taurus/src/implementation/boolean.rs +++ b/taurus/src/implementation/boolean.rs @@ -1,634 +1,327 @@ +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use crate::{context::Context, error::RuntimeError}; use tucana::shared::{Value, value::Kind}; -pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { 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), + ("std::boolean::as_number", HandlerFn::eager(as_number, 1)), + ("std::boolean::as_text", HandlerFn::eager(as_text, 1)), + ("std::boolean::from_number", HandlerFn::eager(from_number, 1)), + ("std::boolean::from_text", HandlerFn::eager(from_text, 1)), + ("std::boolean::is_equal", HandlerFn::eager(is_equal, 2)), + ("std::boolean::negate", HandlerFn::eager(negate, 1)), ] } -fn as_number(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a boolean value but received {:?}", values), - )); - }; - +fn as_number(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: bool); Signal::Success(Value { - kind: Some(Kind::NumberValue(*value as i64 as f64)), + kind: Some(Kind::NumberValue((value as i64) as f64)), }) } -fn as_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a boolean value but received {:?}", values), - )); - }; - +fn as_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: bool); Signal::Success(Value { kind: Some(Kind::StringValue(value.to_string())), }) } -fn from_number(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(number)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number value but received {:?}", values), - )); - }; - - let is_zero = number == &0.0; - +fn from_number( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => number: f64); + let is_zero = number == 0.0; Signal::Success(Value { kind: Some(Kind::BoolValue(!is_zero)), }) } -fn from_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(text)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a string value but received {:?}", values), - )); - }; - - let bool: bool = match text.to_lowercase().parse() { - Ok(value) => value, - Err(_) => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Failed to parse boolean from string: {:?}", text), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::BoolValue(bool)), - }) -} +fn from_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => text: String); -fn is_equal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(lhs)), - }, - Value { - kind: Some(Kind::BoolValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( + match text.to_lowercase().parse::() { + Ok(b) => Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }), + Err(_) => Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected two boolean values but received {:?}", values), - )); - }; + format!("Failed to parse boolean from string: {:?}", text), + )), + } +} +fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: bool, rhs: bool); Signal::Success(Value { kind: Some(Kind::BoolValue(lhs == rhs)), }) } -fn negate(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::BoolValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a boolean value but received {:?}", values), - )); - }; - +fn negate(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: bool); Signal::Success(Value { kind: Some(Kind::BoolValue(!value)), }) } - #[cfg(test)] mod tests { use super::*; use crate::context::Context; use tucana::shared::{Value, value::Kind}; - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } + // ---- helpers: make Arguments ---- + fn a_bool(b: bool) -> Argument { + Argument::Eval(Value { kind: Some(Kind::BoolValue(b)) }) + } + fn a_num(n: f64) -> Argument { + Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) + } + fn a_str(s: &str) -> Argument { + Argument::Eval(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)), + // ---- helpers: unwrap Signal ---- + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + other => panic!("Expected NumberValue, got {:?}", other), } } - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + other => panic!("Expected StringValue, got {:?}", other), + } + } + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + other => panic!("Expected BoolValue, got {:?}", other), } } - // Helper function to create an invalid value (no kind) - fn create_invalid_value() -> Value { - Value { kind: None } + // dummy `run` closure (unused by these handlers) + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { kind: Some(Kind::BoolValue(true)) }) } + // ---- tests ---- + #[test] fn test_as_number_success() { let mut ctx = Context::new(); + let mut run = dummy_run; + assert_eq!(expect_num(as_number(&[a_bool(true)], &mut ctx, &mut run)), 1.0); - // Test true -> 1.0 - let values = vec![create_bool_value(true)]; - let signal = as_number(&values, &mut ctx); + let mut run = dummy_run; + assert_eq!(expect_num(as_number(&[a_bool(false)], &mut ctx, &mut run)), 0.0); + } - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; + #[test] + fn test_as_number_errors() { + let mut ctx = Context::new(); - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), + // wrong arity: none + let mut run = dummy_run; + match as_number(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), } - // Test false -> 0.0 - let values = vec![create_bool_value(false)]; - let signal = as_number(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), + // wrong type + let mut run = dummy_run; + match as_number(&[a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool, got {:?}", s), + } + + // too many args + let mut run = dummy_run; + match as_number(&[a_bool(true), a_bool(false)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), } } #[test] - fn test_as_number_error() { + fn test_as_text_success() { let mut ctx = Context::new(); - // Test with wrong number of parameters (none) - let values = vec![]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = as_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert_eq!(expect_str(as_text(&[a_bool(true)], &mut ctx, &mut run)), "true"); + + let mut run = dummy_run; + assert_eq!(expect_str(as_text(&[a_bool(false)], &mut ctx, &mut run)), "false"); } #[test] - fn test_as_text_success() { + fn test_as_text_errors() { let mut ctx = Context::new(); - // Test true -> "true" - let values = vec![create_bool_value(true)]; - let signal = as_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "true"), - _ => panic!("Expected StringValue"), + let mut run = dummy_run; + match as_text(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), } - // Test false -> "false" - let values = vec![create_bool_value(false)]; - let signal = as_text(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::StringValue(val)) => assert_eq!(val, "false"), - _ => panic!("Expected StringValue"), + let mut run = dummy_run; + match as_text(&[a_num(5.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool, got {:?}", s), + } + + let mut run = dummy_run; + match as_text(&[a_bool(true), a_bool(false)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), } } #[test] - fn test_as_text_error() { + fn test_from_number_success() { let mut ctx = Context::new(); - // Test with wrong number of parameters (none) - let values = vec![]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = as_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert_eq!(expect_bool(from_number(&[a_num(0.0)], &mut ctx, &mut run)), false); + + let mut run = dummy_run; + assert_eq!(expect_bool(from_number(&[a_num(3.5)], &mut ctx, &mut run)), true); + + let mut run = dummy_run; + assert_eq!(expect_bool(from_number(&[a_num(-2.0)], &mut ctx, &mut run)), true); + + // -0.0 should behave like 0.0 + let mut run = dummy_run; + assert_eq!(expect_bool(from_number(&[a_num(-0.0)], &mut ctx, &mut run)), false); } #[test] - fn test_from_number_success() { + fn test_from_number_errors() { let mut ctx = Context::new(); - // Test 0.0 -> false - let values = vec![create_number_value(0.0)]; - let signal = from_number(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match from_number(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), } - // Test positive number -> true - let values = vec![create_number_value(5.0)]; - let signal = from_number(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match from_number(&[a_bool(true)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-number, got {:?}", s), } - // Test negative number -> true - let values = vec![create_number_value(-3.5)]; - let signal = from_number(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - 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 signal = from_number(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match from_number(&[a_num(1.0), a_num(2.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), } } #[test] - fn test_from_number_error() { + fn test_from_text_success_and_errors() { let mut ctx = Context::new(); - // Test with wrong number of parameters (none) - let values = vec![]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (bool) - let values = vec![create_bool_value(true)]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = from_number(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } + // success (case-insensitive) + let mut run = dummy_run; + assert_eq!(expect_bool(from_text(&[a_str("true")], &mut ctx, &mut run)), true); - #[test] - fn test_from_text_success() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert_eq!(expect_bool(from_text(&[a_str("FALSE")], &mut ctx, &mut run)), false); - // Test "true" -> true - let values = vec![create_string_value("true")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), + // errors + let mut run = dummy_run; + match from_text(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), } - // Test "false" -> false - let values = vec![create_string_value("false")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match from_text(&[a_str("yes")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for unparseable bool, got {:?}", s), } - // Test "True" -> true (case insensitive) - let values = vec![create_string_value("True")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match from_text(&[a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-string, got {:?}", s), } - // Test "FALSE" -> false (case insensitive) - let values = vec![create_string_value("FALSE")]; - let signal = from_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match from_text(&[a_str("true"), a_str("false")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), } } #[test] - fn test_from_text_error() { + fn test_is_equal_and_errors() { let mut ctx = Context::new(); - // Test with wrong number of parameters (none) - let values = vec![]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (bool) - let values = vec![create_bool_value(true)]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with unparseable text - let values = vec![create_string_value("hello")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with numeric string - let values = vec![create_string_value("123")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with empty string - let values = vec![create_string_value("")]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } + // equalities + let mut run = dummy_run; + assert_eq!(expect_bool(is_equal(&[a_bool(true), a_bool(true)], &mut ctx, &mut run)), true); - #[test] - fn test_is_equal_success() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert_eq!(expect_bool(is_equal(&[a_bool(false), a_bool(false)], &mut ctx, &mut run)), true); - // Test true == true -> true - let values = vec![create_bool_value(true), create_bool_value(true)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert_eq!(expect_bool(is_equal(&[a_bool(true), a_bool(false)], &mut ctx, &mut run)), false); - // Test false == false -> true - let values = vec![create_bool_value(false), create_bool_value(false)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), + // arity/type errors + let mut run = dummy_run; + match is_equal(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), } - // Test true == false -> false - let values = vec![create_bool_value(true), create_bool_value(false)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match is_equal(&[a_bool(true)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 1, got {:?}", s), } - // Test false == true -> false - let values = vec![create_bool_value(false), create_bool_value(true)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match is_equal(&[a_bool(true), a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool rhs, got {:?}", s), } } #[test] - fn test_is_equal_error() { + fn test_negate_success_and_errors() { let mut ctx = Context::new(); - // Test with wrong number of parameters (none) - let values = vec![]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong number of parameters (one) - let values = vec![create_bool_value(true)]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![create_invalid_value(), create_bool_value(true)]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - let values = vec![create_bool_value(true), create_invalid_value()]; - let result = is_equal(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_bool(negate(&[a_bool(true)], &mut ctx, &mut run)), false); - #[test] - fn test_negate_success() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert_eq!(expect_bool(negate(&[a_bool(false)], &mut ctx, &mut run)), true); - // Test !true -> false - let values = vec![create_bool_value(true)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + // errors + let mut run = dummy_run; + match negate(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 0, got {:?}", s), } - // Test !false -> true - let values = vec![create_bool_value(false)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), + let mut run = dummy_run; + match negate(&[a_num(1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for non-bool, got {:?}", s), } - } - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - - // 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (number) - let values = vec![create_number_value(5.0)]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value type (string) - let values = vec![create_string_value("hello")]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = negate(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + match negate(&[a_bool(true), a_bool(false)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for arity 2, got {:?}", s), + } } } diff --git a/taurus/src/implementation/control.rs b/taurus/src/implementation/control.rs index 4941ebe..8014dac 100644 --- a/taurus/src/implementation/control.rs +++ b/taurus/src/implementation/control.rs @@ -1,42 +1,48 @@ +use crate::context::Context; +use crate::context::argument::Argument; +use crate::context::argument::ParameterNode::{Eager, Lazy}; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use crate::error::RuntimeError; use tucana::shared::Value; use tucana::shared::value::Kind; -pub fn collect_control_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_control_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("std::control::stop", stop), - ("std::control::return", r#return), - ("std::control::if", r#if), - ("std::control::if_else", if_else), + ("std::control::stop", HandlerFn::eager(stop, 0)), + ("std::control::return", HandlerFn::eager(r#return, 1)), + ( + "std::control::if", + HandlerFn::into_function_entry(r#if, vec![Eager, Lazy]), + ), + ( + "std::control::if_else", + HandlerFn::into_function_entry(if_else, vec![Eager, Lazy, Lazy]), + ), ] } -fn stop(_values: &[Value], _ctx: &mut Context) -> Signal { +fn stop(_args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { Signal::Stop } -fn r#return(values: &[Value], _ctx: &mut Context) -> Signal { - let [Value { kind }] = values else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one generic value but received {:?}", values), - )); - }; - - Signal::Return(Value { kind: kind.clone() }) +fn r#return(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: Value); + Signal::Return(value) } -fn r#if(values: &[Value], _ctx: &mut Context) -> Signal { +fn r#if(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { let [ - Value { + Argument::Eval(Value { kind: Some(Kind::StringValue(text)), - } - ] = values + }), + Argument::Thunk(if_pointer), + ] = args else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected a string value but received {:?}", values), + format!("Expected a string value but received {:?}", args), )); }; @@ -51,24 +57,26 @@ fn r#if(values: &[Value], _ctx: &mut Context) -> Signal { }; if bool { - // Signal::Skip(vec![0]) + _run(*if_pointer) } else { - // Signal::Return(Value { - // kind: Some(Kind::NullValue(0)), - // }) + Signal::Return(Value { + kind: Some(Kind::NullValue(0)), + }) } } -fn if_else(values: &[Value], _ctx: &mut Context) -> Signal { +fn if_else(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { let [ - Value { + Argument::Eval(Value { kind: Some(Kind::StringValue(text)), - } - ] = values + }), + Argument::Thunk(if_pointer), + Argument::Thunk(else_pointer), + ] = args else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected a string value but received {:?}", values), + format!("Expected a string value but received {:?}", args), )); }; @@ -83,8 +91,8 @@ fn if_else(values: &[Value], _ctx: &mut Context) -> Signal { }; if bool { - // Signal::Skip(vec![1]) + _run(*if_pointer) } else { - // Signal::Skip(vec![0]) + _run(*else_pointer) } } diff --git a/taurus/src/implementation/http.rs b/taurus/src/implementation/http.rs index d1605e0..df13489 100644 --- a/taurus/src/implementation/http.rs +++ b/taurus/src/implementation/http.rs @@ -1,31 +1,22 @@ use crate::context::Context; use crate::context::signal::Signal; use crate::error::RuntimeError; -use crate::registry::HandlerFn; use tucana::shared::value::Kind; use tucana::shared::{Struct, Value}; +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; -pub fn collect_http_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_http_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("http::request::create", create_request), - ("http::response::create", create_response), - ("http::control::respond", respond), + ("http::request::create", HandlerFn::eager(create_request, 1)), + ("http::response::create", HandlerFn::eager(create_response, 4)), + ("http::control::respond", HandlerFn::eager(respond, 3)), ] } -fn respond(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(struct_val)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected exactly one response struct, got {:?}", values), - )); - }; - +fn respond(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => struct_val: Struct); let fields = &struct_val.fields; let Some(headers_val) = fields.get("headers") else { @@ -75,29 +66,8 @@ fn respond(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn create_request(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(http_method)), - }, - Value { - kind: Some(Kind::StructValue(headers)), - }, - Value { - kind: Some(Kind::StringValue(http_url)), - }, - payload, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected [method, headers, url, payload] but received {:?}", - values - ), - )); - }; - +fn create_request(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => http_method: String, headers: Struct, http_url: String, payload: Value); let mut fields = std::collections::HashMap::new(); fields.insert( @@ -127,32 +97,14 @@ fn create_request(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn create_response(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(http_status_code)), - }, - Value { - kind: Some(Kind::StructValue(headers)), - }, - payload, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected [http_status_code, headers, payload] but received {:?}", - values - ), - )); - }; - +fn create_response(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => http_status_code: f64, headers: Struct, payload: Value); let mut fields = std::collections::HashMap::new(); fields.insert( "status_code".to_string(), Value { - kind: Some(Kind::NumberValue(*http_status_code)), + kind: Some(Kind::NumberValue(http_status_code)), }, ); diff --git a/taurus/src/implementation/mod.rs b/taurus/src/implementation/mod.rs index 9067f60..d2ad25e 100644 --- a/taurus/src/implementation/mod.rs +++ b/taurus/src/implementation/mod.rs @@ -1,4 +1,4 @@ -use crate::context::registry::HandlerFn; +use crate::context::registry::{HandlerFunctionEntry}; mod array; mod boolean; @@ -8,7 +8,7 @@ mod number; mod object; mod text; -pub fn collect() -> Vec<(&'static str, HandlerFn)> { +pub fn collect() -> Vec<(&'static str, HandlerFunctionEntry)> { let mut result = vec![]; result.extend(array::collect_array_functions()); diff --git a/taurus/src/implementation/number.rs b/taurus/src/implementation/number.rs index b61f918..d415cd1 100644 --- a/taurus/src/implementation/number.rs +++ b/taurus/src/implementation/number.rs @@ -2,144 +2,80 @@ use std::f64; use tucana::shared::{Value, value::Kind}; +use crate::context::argument::Argument; +use crate::context::macros::{args, no_args}; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; use crate::{context::Context, error::RuntimeError}; -pub fn collect_number_functions() -> Vec<(&'static str, )> { +pub fn collect_number_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("std::number::add", add), - ("std::number::multiply", multiply), - ("std::number::substract", substract), - ("std::number::divide", divide), - ("std::number::modulo", modulo), - ("std::number::abs", abs), - ("std::number::is_positive", is_positive), - ("std::number::is_greater", is_greater), - ("std::number::is_less", is_less), - ("std::number::is_zero", is_zero), - ("std::number::square", square), - ("std::number::exponential", exponential), - ("std::number::pi", pi), - ("std::number::euler", euler), - ("std::number::infinity", infinity), - ("std::number::round_up", round_up), - ("std::number::round_down", round_down), - ("std::number::round", round), - ("std::number::square_root", square_root), - ("std::number::root", root), - ("std::number::log", log), - ("std::number::ln", ln), - ("std::number::from_text", from_text), - ("std::number::as_text", as_text), - ("std::number::min", min), - ("std::number::max", max), - ("std::number::negate", negate), - ("std::number::random", random), - ("std::number::sin", sin), - ("std::number::cos", cos), - ("std::number::tan", tan), - ("std::number::arcsin", arcsin), - ("std::number::arccos", arccos), - ("std::number::arctan", arctan), - ("std::number::sinh", sinh), - ("std::number::cosh", cosh), - ("std::number::clamp", clamp), - ("std::number::is_equal", is_equal), + ("std::number::add", HandlerFn::eager(add, 2)), + ("std::number::multiply", HandlerFn::eager(multiply, 2)), + ("std::number::substract", HandlerFn::eager(substract, 2)), + ("std::number::divide", HandlerFn::eager(divide, 2)), + ("std::number::modulo", HandlerFn::eager(modulo, 2)), + ("std::number::abs", HandlerFn::eager(abs, 1)), + ("std::number::is_positive", HandlerFn::eager(is_positive, 1)), + ("std::number::is_greater", HandlerFn::eager(is_greater, 2)), + ("std::number::is_less", HandlerFn::eager(is_less, 2)), + ("std::number::is_zero", HandlerFn::eager(is_zero, 1)), + ("std::number::square", HandlerFn::eager(square, 2)), + ("std::number::exponential", HandlerFn::eager(exponential, 2)), + ("std::number::pi", HandlerFn::eager(pi, 0)), + ("std::number::euler", HandlerFn::eager(euler, 0)), + ("std::number::infinity", HandlerFn::eager(infinity, 0)), + ("std::number::round_up", HandlerFn::eager(round_up, 2)), + ("std::number::round_down", HandlerFn::eager(round_down, 2)), + ("std::number::round", HandlerFn::eager(round, 2)), + ("std::number::square_root", HandlerFn::eager(square_root, 1)), + ("std::number::root", HandlerFn::eager(root, 2)), + ("std::number::log", HandlerFn::eager(log, 2)), + ("std::number::ln", HandlerFn::eager(ln, 1)), + ("std::number::from_text", HandlerFn::eager(from_text, 1)), + ("std::number::as_text", HandlerFn::eager(as_text, 1)), + ("std::number::min", HandlerFn::eager(min, 2)), + ("std::number::max", HandlerFn::eager(max, 2)), + ("std::number::negate", HandlerFn::eager(negate, 1)), + ("std::number::random", HandlerFn::eager(random, 2)), + ("std::number::sin", HandlerFn::eager(sin, 1)), + ("std::number::cos", HandlerFn::eager(cos, 1)), + ("std::number::tan", HandlerFn::eager(tan, 1)), + ("std::number::arcsin", HandlerFn::eager(arcsin, 1)), + ("std::number::arccos", HandlerFn::eager(arccos, 1)), + ("std::number::arctan", HandlerFn::eager(arctan, 1)), + ("std::number::sinh", HandlerFn::eager(sinh, 1)), + ("std::number::cosh", HandlerFn::eager(cosh, 1)), + ("std::number::clamp", HandlerFn::eager(clamp, 3)), + ("std::number::is_equal", HandlerFn::eager(is_equal, 2)), ] } -fn add(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn add(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(lhs + rhs)), }) } -fn multiply(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn multiply(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(lhs * rhs)), }) } -fn substract(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn substract(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(lhs - rhs)), }) } -fn divide(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; +fn divide(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); - if rhs == &0.0 { + if rhs == 0.0 { return Signal::Failure(RuntimeError::simple_str( "DivisionByZero", "You cannot divide by zero", @@ -151,26 +87,10 @@ fn divide(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn modulo(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; +fn modulo(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); - if rhs == &0.0 { + if rhs == 0.0 { return Signal::Failure(RuntimeError::simple_str( "DivisionByZero", "You cannot divide by zero", @@ -182,1770 +102,532 @@ fn modulo(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn abs(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - +fn abs(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.abs())), }) } -fn is_positive(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - +fn is_positive( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::BoolValue(!value.is_sign_negative())), }) } -fn is_greater(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn is_greater( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { kind: Some(Kind::BoolValue(lhs > rhs)), }) } -fn is_less(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn is_less(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { kind: Some(Kind::BoolValue(lhs < rhs)), }) } -fn is_zero(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - +fn is_zero(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { - kind: Some(Kind::BoolValue(value == &0.0)), + kind: Some(Kind::BoolValue(value == 0.0)), }) } -fn square(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected a number as argument but received {:?}", values), - )); - }; - +fn square(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.powf(2.0))), }) } -fn exponential(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(base)), - }, - Value { - kind: Some(Kind::NumberValue(exponent)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn exponential( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => base: f64, exponent: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(base.powf(*exponent))), + kind: Some(Kind::NumberValue(base.powf(exponent))), }) } -fn pi(_values: &[Value], _ctx: &mut Context) -> Signal { +fn pi(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + no_args!(args); Signal::Success(Value { kind: Some(Kind::NumberValue(f64::consts::PI)), }) } -fn euler(_values: &[Value], _ctx: &mut Context) -> Signal { +fn euler(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + no_args!(args); Signal::Success(Value { kind: Some(Kind::NumberValue(f64::consts::E)), }) } -fn infinity(_values: &[Value], _ctx: &mut Context) -> Signal { +fn infinity(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + no_args!(args); Signal::Success(Value { kind: Some(Kind::NumberValue(f64::INFINITY)), }) } -fn round_up(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(decimal_places)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let factor = 10_f64.powi(*decimal_places as i32); - +fn round_up(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, decimal_places: f64); + let factor = 10_f64.powi(decimal_places as i32); Signal::Success(Value { kind: Some(Kind::NumberValue((value * factor).ceil() / factor)), }) } -fn round_down(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(decimal_places)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let factor = 10_f64.powi(*decimal_places as i32); - +fn round_down( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: f64, decimal_places: f64); + let factor = 10_f64.powi(decimal_places as i32); Signal::Success(Value { kind: Some(Kind::NumberValue((value * factor).floor() / factor)), }) } -fn round(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(decimal_places)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - - let factor = 10_f64.powi(*decimal_places as i32); - +fn round(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, decimal_places: f64); + let factor = 10_f64.powi(decimal_places as i32); Signal::Success(Value { kind: Some(Kind::NumberValue((value * factor).round() / factor)), }) } -fn square_root(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn square_root( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.sqrt())), }) } -fn root(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(root)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn root(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, root: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(value.powf(*root))), + kind: Some(Kind::NumberValue(value.powf(root))), }) } -fn log(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(log)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn log(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, base: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(value.log(*log))), + kind: Some(Kind::NumberValue(value.log(base))), }) } -fn ln(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn ln(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.ln())), }) } -fn from_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(string_value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one string as argument but received {:?}", values), - )); - }; - - let value: f64 = match string_value.parse() { - Ok(result) => result, - Err(_) => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Failed to parse string as number: {}", string_value), - )); - } - }; - - Signal::Success(Value { - kind: Some(Kind::NumberValue(value)), - }) -} +fn from_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => string_value: String); -fn as_text(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( + match string_value.parse::() { + Ok(v) => Signal::Success(Value { + kind: Some(Kind::NumberValue(v)), + }), + Err(_) => Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; + format!("Failed to parse string as number: {}", string_value), + )), + } +} +fn as_text(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::StringValue(value.to_string())), }) } -fn min(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn min(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs.min(*rhs))), + kind: Some(Kind::NumberValue(lhs.min(rhs))), }) } -fn max(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn max(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(lhs.max(*rhs))), + kind: Some(Kind::NumberValue(lhs.max(rhs))), }) } -fn negate(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn negate(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(-value)), }) } -fn random(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(min)), - }, - Value { - kind: Some(Kind::NumberValue(max)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn random(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => min: f64, max: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(rand::random_range(*min..*max))), + kind: Some(Kind::NumberValue(rand::random_range(min..max))), }) } -fn sin(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn sin(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.sin())), }) } -fn cos(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn cos(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.cos())), }) } -fn tan(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn tan(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.tan())), }) } -fn arcsin(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn arcsin(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.asin())), }) } -fn arccos(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn arccos(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.acos())), }) } -fn arctan(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn arctan(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.atan())), }) } -fn sinh(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; +fn sinh(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.sinh())), }) } -fn cosh(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one number as argument but received {:?}", values), - )); - }; - +fn cosh(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64); Signal::Success(Value { kind: Some(Kind::NumberValue(value.cosh())), }) } -fn clamp(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(min)), - }, - Value { - kind: Some(Kind::NumberValue(max)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three numbers as arguments but received {:?}", - values - ), - )); - }; - +fn clamp(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: f64, min: f64, max: f64); Signal::Success(Value { - kind: Some(Kind::NumberValue(value.clamp(*min, *max))), + kind: Some(Kind::NumberValue(value.clamp(min, max))), }) } -fn is_equal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::NumberValue(lhs)), - }, - Value { - kind: Some(Kind::NumberValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two numbers as arguments but received {:?}", - values - ), - )); - }; - +fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: f64, rhs: f64); Signal::Success(Value { kind: Some(Kind::BoolValue(lhs == rhs)), }) } - #[cfg(test)] mod tests { use super::*; use crate::context::Context; + use crate::context::argument::Argument; use tucana::shared::{Value, value::Kind}; - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } + // ---- helpers: Arguments ---- + fn a_num(n: f64) -> Argument { + Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) } - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { + fn a_str(s: &str) -> Argument { + Argument::Eval(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)), + // ---- helpers: extractors ---- + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + other => panic!("Expected NumberValue, got {:?}", other), } } - - // 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 signal = add(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), - _ => panic!("Expected NumberValue"), + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + other => panic!("Expected BoolValue, got {:?}", other), } } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong value types - let values = vec![create_string_value("hello"), create_number_value(3.0)]; - let result = add(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![create_invalid_value(), create_number_value(3.0)]; - let result = add(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_multiply_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(4.0), create_number_value(2.5)]; - let signal = multiply(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 10.0), - _ => panic!("Expected NumberValue"), + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + other => panic!("Expected StringValue, got {:?}", other), } } - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) } #[test] - fn test_substract_success() { + fn test_add_and_multiply() { let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(4.0)]; - let signal = substract(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 6.0), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + assert_eq!(expect_num(add(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run)), 8.0); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert_eq!(expect_num(multiply(&[a_num(4.0), a_num(2.5)], &mut ctx, &mut run)), 10.0); } #[test] - fn test_divide_success() { + fn test_substract_and_divide() { let mut ctx = Context::new(); - let values = vec![create_number_value(15.0), create_number_value(3.0)]; - let signal = divide(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), - _ => panic!("Expected NumberValue"), - } - } - #[test] - fn test_divide_by_zero_exception() { - let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(0.0)]; - let result = divide(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_num(substract(&[a_num(10.0), a_num(4.0)], &mut ctx, &mut run)), 6.0); - #[test] - fn test_divide_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("not_a_number"), - create_number_value(2.0), - ]; - let result = divide(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_num(divide(&[a_num(15.0), a_num(3.0)], &mut ctx, &mut run)), 5.0); - #[test] - fn test_modulo_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(10.0), create_number_value(3.0)]; - let signal = modulo(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 1.0), - _ => panic!("Expected NumberValue"), + // divide by zero -> Failure + let mut run = dummy_run; + match divide(&[a_num(10.0), a_num(0.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure on divide by zero, got {:?}", s), } } #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_abs_success() { + fn test_modulo_and_abs() { let mut ctx = Context::new(); - // Test positive number - let values = vec![create_number_value(5.0)]; - let signal = abs(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 5.0), - _ => panic!("Expected NumberValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(modulo(&[a_num(10.0), a_num(3.0)], &mut ctx, &mut run)), 1.0); - // Test negative number - let values = vec![create_number_value(-7.5)]; - let signal = abs(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 7.5), - _ => panic!("Expected NumberValue"), + // modulo by zero -> Failure + let mut run = dummy_run; + match modulo(&[a_num(10.0), a_num(0.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure on modulo by zero, got {:?}", s), } - } - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert_eq!(expect_num(abs(&[a_num(-7.5)], &mut ctx, &mut run)), 7.5); } #[test] - fn test_is_positive_success() { + fn test_comparisons_and_zero() { let mut ctx = Context::new(); - // Test positive number - let values = vec![create_number_value(5.0)]; - let signal = is_positive(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = is_positive(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert!(expect_bool(is_positive(&[a_num(5.0)], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(!expect_bool(is_positive(&[a_num(-1.0)], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(expect_bool(is_positive(&[a_num(0.0)], &mut ctx, &mut run))); - // Test zero - let values = vec![create_number_value(0.0)]; - let signal = is_positive(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - } + let mut run = dummy_run; + assert!(expect_bool(is_greater(&[a_num(10.0), a_num(5.0)], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(expect_bool(is_less(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run))); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert!(expect_bool(is_zero(&[a_num(0.0)], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(!expect_bool(is_zero(&[a_num(0.01)], &mut ctx, &mut run))); } #[test] - fn test_is_greater_success() { + fn test_powers_and_exponential() { let mut ctx = Context::new(); - // Test greater - let values = vec![create_number_value(10.0), create_number_value(5.0)]; - let signal = is_greater(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(square(&[a_num(4.0)], &mut ctx, &mut run)), 16.0); - // Test not greater - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = is_greater(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(exponential(&[a_num(2.0), a_num(3.0)], &mut ctx, &mut run)), 8.0); } #[test] - fn test_is_greater_runtime_exception() { + fn test_constants() { 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_is_less_success() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert!((expect_num(pi(&[], &mut ctx, &mut run)) - std::f64::consts::PI).abs() < f64::EPSILON); - // Test less - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = is_less(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert!((expect_num(euler(&[], &mut ctx, &mut run)) - std::f64::consts::E).abs() < f64::EPSILON); - // Test not less - let values = vec![create_number_value(10.0), create_number_value(5.0)]; - let signal = is_less(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + let inf = expect_num(infinity(&[], &mut ctx, &mut run)); + assert!(inf.is_infinite() && inf.is_sign_positive()); } #[test] - fn test_is_less_runtime_exception() { + fn test_rounding() { let mut ctx = Context::new(); - let values = vec![create_invalid_value(), create_number_value(5.0)]; - let result = is_less(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_is_zero_success() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert_eq!(expect_num(round_up(&[a_num(f64::consts::PI), a_num(2.0)], &mut ctx, &mut run)), 3.15); - // Test zero - let values = vec![create_number_value(0.0)]; - let signal = is_zero(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(round_down(&[a_num(f64::consts::PI), a_num(2.0)], &mut ctx, &mut run)), 3.14); - // Test non-zero - let values = vec![create_number_value(5.0)]; - let signal = is_zero(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(round(&[a_num(3.145), a_num(2.0)], &mut ctx, &mut run)), 3.15); } #[test] - fn test_is_zero_runtime_exception() { + fn test_roots_and_logs() { let mut ctx = Context::new(); - let values = vec![create_string_value("zero")]; - let result = is_zero(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_square_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(4.0)]; - let signal = square(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 16.0), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + assert_eq!(expect_num(square_root(&[a_num(16.0)], &mut ctx, &mut run)), 4.0); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } + // cube root via exponent 1/3 + let mut run = dummy_run; + let r = expect_num(root(&[a_num(8.0), a_num(1.0/3.0)], &mut ctx, &mut run)); + assert!((r - 2.0).abs() < 1e-6); - #[test] - fn test_exponential_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(2.0), create_number_value(3.0)]; - let signal = exponential(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 8.0), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + let lg = expect_num(log(&[a_num(100.0), a_num(10.0)], &mut ctx, &mut run)); + assert!((lg - 2.0).abs() < f64::EPSILON); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + let ln1 = expect_num(ln(&[a_num(f64::consts::E)], &mut ctx, &mut run)); + assert!((ln1 - 1.0).abs() < f64::EPSILON); } #[test] - fn test_pi_success() { + fn test_text_conversions() { let mut ctx = Context::new(); - let values = vec![]; - let signal = pi(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = euler(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = infinity(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = round_up(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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 signal = round_down(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_round_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.14159), create_number_value(2.0)]; - let signal = round(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_square_root_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(16.0)]; - let signal = square_root(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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 signal = root(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_log_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(100.0), create_number_value(10.0)]; - let signal = log(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_ln_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(std::f64::consts::E)]; - let signal = ln(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_from_text_success() { - let mut ctx = Context::new(); - let values = vec![create_string_value("42.5")]; - let signal = from_text(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong type - let values = vec![create_number_value(42.0)]; - let result = from_text(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_as_text_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(42.5)]; - let signal = as_text(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_min_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = min(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_max_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(3.0), create_number_value(7.0)]; - let signal = max(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_negate_success() { - let mut ctx = Context::new(); - - // Test positive number - let values = vec![create_number_value(5.0)]; - let signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = negate(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_random_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0), create_number_value(10.0)]; - let signal = random(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!(val >= 1.0 && val < 10.0); - } - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + assert_eq!(expect_num(from_text(&[a_str("42.5")], &mut ctx, &mut run)), 42.5); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_str(as_text(&[a_num(42.5)], &mut ctx, &mut run)), "42.5".to_string()); - #[test] - fn test_sin_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(std::f64::consts::PI / 2.0)]; - let signal = sin(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), + // from_text failure + let mut run = dummy_run; + match from_text(&[a_str("not_a_number")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for invalid parse, got {:?}", s), } } #[test] - fn test_sin_runtime_exception() { + fn test_min_max_and_negate() { let mut ctx = Context::new(); - let values = vec![create_bool_value(true)]; - let result = sin(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_cos_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(0.0)]; - let signal = cos(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + assert_eq!(expect_num(min(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), 3.0); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_num(max(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), 7.0); - #[test] - fn test_tan_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(std::f64::consts::PI / 4.0)]; - let signal = tan(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < 0.0001), - _ => panic!("Expected NumberValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(negate(&[a_num(5.0)], &mut ctx, &mut run)), -5.0); } #[test] - fn test_tan_runtime_exception() { + fn test_random_range() { let mut ctx = Context::new(); - let values = vec![create_invalid_value()]; - let result = tan(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_arcsin_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0)]; - let signal = arcsin(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!((val - std::f64::consts::PI / 2.0).abs() < f64::EPSILON) - } - _ => panic!("Expected NumberValue"), - } + let mut run = dummy_run; + let r = expect_num(random(&[a_num(1.0), a_num(10.0)], &mut ctx, &mut run)); + assert!(r >= 1.0 && r < 10.0); } #[test] - fn test_arcsin_runtime_exception() { + fn test_trig_and_hyperbolic() { let mut ctx = Context::new(); - let values = vec![create_bool_value(false)]; - let result = arcsin(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_arccos_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0)]; - let signal = arccos(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!(val.abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + let s = expect_num(sin(&[a_num(f64::consts::PI/2.0)], &mut ctx, &mut run)); + assert!((s - 1.0).abs() < 1e-12); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + let c = expect_num(cos(&[a_num(0.0)], &mut ctx, &mut run)); + assert!((c - 1.0).abs() < 1e-12); - #[test] - fn test_arctan_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(1.0)]; - let signal = arctan(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => { - assert!((val - std::f64::consts::PI / 4.0).abs() < f64::EPSILON) - } - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + let t = expect_num(tan(&[a_num(f64::consts::PI/4.0)], &mut ctx, &mut run)); + assert!((t - 1.0).abs() < 1e-4); - #[test] - fn test_arctan_runtime_exception() { - let mut ctx = Context::new(); - let values = vec![create_invalid_value()]; - let result = arctan(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + let asn = expect_num(arcsin(&[a_num(1.0)], &mut ctx, &mut run)); + assert!((asn - f64::consts::PI/2.0).abs() < 1e-12); - #[test] - fn test_sinh_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(0.0)]; - let signal = sinh(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!(val.abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + let acs = expect_num(arccos(&[a_num(1.0)], &mut ctx, &mut run)); + assert!(acs.abs() < 1e-12); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + let atn = expect_num(arctan(&[a_num(1.0)], &mut ctx, &mut run)); + assert!((atn - f64::consts::PI/4.0).abs() < 1e-12); - #[test] - fn test_cosh_success() { - let mut ctx = Context::new(); - let values = vec![create_number_value(0.0)]; - let signal = cosh(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::NumberValue(val)) => assert!((val - 1.0).abs() < f64::EPSILON), - _ => panic!("Expected NumberValue"), - } - } + let mut run = dummy_run; + let sh = expect_num(sinh(&[a_num(0.0)], &mut ctx, &mut run)); + assert!(sh.abs() < 1e-12); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + let ch = expect_num(cosh(&[a_num(0.0)], &mut ctx, &mut run)); + assert!((ch - 1.0).abs() < 1e-12); } #[test] - fn test_clamp_success() { + fn test_clamp_and_is_equal() { 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 signal = clamp(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = clamp(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = clamp(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_num(clamp(&[a_num(5.0), a_num(1.0), a_num(10.0)], &mut ctx, &mut run)), 5.0); - #[test] - fn test_is_equal_success() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert_eq!(expect_num(clamp(&[a_num(-5.0), a_num(1.0), a_num(10.0)], &mut ctx, &mut run)), 1.0); - // Test equal numbers - let values = vec![create_number_value(5.0), create_number_value(5.0)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } + let mut run = dummy_run; + assert_eq!(expect_num(clamp(&[a_num(15.0), a_num(1.0), a_num(10.0)], &mut ctx, &mut run)), 10.0); - // Test unequal numbers - let values = vec![create_number_value(5.0), create_number_value(3.0)]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } - } + let mut run = dummy_run; + assert!(expect_bool(is_equal(&[a_num(5.0), a_num(5.0)], &mut ctx, &mut run))); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert!(!expect_bool(is_equal(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run))); } } diff --git a/taurus/src/implementation/object.rs b/taurus/src/implementation/object.rs index db3864a..c1060b8 100644 --- a/taurus/src/implementation/object.rs +++ b/taurus/src/implementation/object.rs @@ -1,78 +1,38 @@ -use tucana::shared::{Value, value::Kind}; +use tucana::shared::{Value, value::Kind, Struct}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, registry::HandlerFn}; +use crate::{context::Context, error::RuntimeError}; +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; -pub fn collect_object_functions() -> Vec<(&'static str, HandlerFn)> { +pub fn collect_object_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("std::object::contains_key", contains_key), - ("std::object::keys", keys), - ("std::object::size", size), - ("std::object::set", set), + ("std::object::contains_key", HandlerFn::eager(contains_key, 2)), + ("std::object::keys", HandlerFn::eager(keys, 1)), + ("std::object::size", HandlerFn::eager(size, 1)), + ("std::object::set", HandlerFn::eager(set, 3)), ] } -fn contains_key(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - Value { - kind: Some(Kind::StringValue(key)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object and a text as arguments but recieved: {:?}", - values - ), - )); - }; - - let contains = object.fields.contains_key(key); +fn contains_key(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct, key: String); + let contains = object.fields.contains_key(&key); Signal::Success(Value { kind: Some(Kind::BoolValue(contains)), }) } -fn size(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object as an argument but received {:?}", - values - ), - )); - }; +fn size(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct); Signal::Success(Value { kind: Some(Kind::NumberValue(object.fields.len() as f64)), }) } -fn keys(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object as an argument but received {:?}", - values - ), - )); - }; +fn keys(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct); let keys = object .fields @@ -87,26 +47,8 @@ fn keys(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn set(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StructValue(object)), - }, - Value { - kind: Some(Kind::StringValue(key)), - }, - value, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected an object as an argument but received {:?}", - values - ), - )); - }; - +fn set(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => object: Struct, key: String, value: Value); let mut new_object = object.clone(); new_object.fields.insert(key.clone(), value.clone()); @@ -114,349 +56,170 @@ fn set(values: &[Value], _ctx: &mut Context) -> Signal { kind: Some(Kind::StructValue(new_object)), }) } - #[cfg(test)] mod tests { use super::*; use crate::context::Context; + use crate::context::argument::Argument; use std::collections::HashMap; - use tucana::shared::{Value, value::Kind}; - - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } - } + use tucana::shared::{Struct as TcStruct, Value, value::Kind}; - // Helper function to create a number value - fn create_number_value(num: f64) -> Value { - Value { - kind: Some(Kind::NumberValue(num)), - } + // ---- helpers: Value builders ---- + fn v_string(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)), - } + fn v_number(n: f64) -> Value { + Value { kind: Some(Kind::NumberValue(n)) } } - - // Helper function to create an object/struct value - fn create_object_value(fields: HashMap) -> Value { - Value { - kind: Some(Kind::StructValue(tucana::shared::Struct { fields })), - } + fn v_bool(b: bool) -> Value { + Value { kind: Some(Kind::BoolValue(b)) } } - - // Helper function to create an empty object - fn create_empty_object() -> Value { - create_object_value(HashMap::new()) + fn v_struct(fields: HashMap) -> Value { + Value { kind: Some(Kind::StructValue(TcStruct { fields })) } } - // Helper function to create a test object with some fields - fn create_test_object() -> Value { - let mut fields = HashMap::new(); - fields.insert("name".to_string(), create_string_value("John")); - fields.insert("age".to_string(), create_number_value(30.0)); - fields.insert("active".to_string(), create_bool_value(true)); - create_object_value(fields) + // ---- helpers: Struct builders (for args that expect Struct) ---- + fn s_empty() -> TcStruct { + TcStruct { fields: HashMap::new() } } - - // Helper function to create an invalid value (no kind) - fn create_invalid_value() -> Value { - Value { kind: None } - } - - #[test] - fn test_contains_key_success() { - let mut ctx = Context::new(); - let test_object = create_test_object(); - - // Test existing key - let values = vec![test_object.clone(), create_string_value("name")]; - let signal = contains_key(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, true), - _ => panic!("Expected BoolValue"), - } - - // Test non-existing key - let values = vec![test_object, create_string_value("nonexistent")]; - let signal = contains_key(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), + fn s_from(mut kv: Vec<(&str, Value)>) -> TcStruct { + let mut map = HashMap::::new(); + for (k, v) in kv.drain(..) { + map.insert(k.to_string(), v); } + TcStruct { fields: map } } - - #[test] - fn test_contains_key_empty_object() { - let mut ctx = Context::new(); - let empty_object = create_empty_object(); - - let values = vec![empty_object, create_string_value("any_key")]; - let signal = contains_key(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::BoolValue(val)) => assert_eq!(val, false), - _ => panic!("Expected BoolValue"), - } + fn s_test() -> TcStruct { + s_from(vec![ + ("name", v_string("John")), + ("age", v_number(30.0)), + ("active", v_bool(true)), + ]) } - #[test] - fn test_contains_key_runtime_exception() { - let mut ctx = Context::new(); - - // Test with wrong number of parameters - let values = vec![create_test_object()]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong first parameter type (not an object) - let values = vec![ - create_string_value("not_an_object"), - create_string_value("key"), - ]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong second parameter type (not a string) - let values = vec![create_test_object(), create_number_value(123.0)]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![create_invalid_value(), create_string_value("key")]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![ - create_test_object(), - create_string_value("key"), - create_string_value("extra"), - ]; - let result = contains_key(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + // ---- helpers: Argument builders ---- + #[allow(dead_code)] + fn a_value(v: Value) -> Argument { Argument::Eval(v) } + fn a_string(s: &str) -> Argument { Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) } + fn a_struct(s: TcStruct) -> Argument { Argument::Eval(Value { kind: Some(Kind::StructValue(s)) }) } + + // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) } #[test] - fn test_size_success() { + fn test_contains_key_success() { let mut ctx = Context::new(); - // Test with object containing fields - let test_object = create_test_object(); - let values = vec![test_object]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 3.0), // name, age, active - _ => panic!("Expected NumberValue"), - } - - // Test with empty object - let empty_object = create_empty_object(); - let values = vec![empty_object]; - let signal = size(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::NumberValue(val)) => assert_eq!(val, 0.0), - _ => panic!("Expected NumberValue"), - } + // existing key + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("name")]; + let signal = contains_key(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::BoolValue(b)) => assert!(b), _ => panic!("Expected BoolValue") } + + // non-existing key + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("nonexistent")]; + let signal = contains_key(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::BoolValue(b)) => assert!(!b), _ => panic!("Expected BoolValue") } + + // empty object + let mut run = dummy_run; + let args = vec![a_struct(s_empty()), a_string("any_key")]; + let signal = contains_key(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::BoolValue(b)) => assert!(!b), _ => panic!("Expected BoolValue") } } #[test] - fn test_size_runtime_exception() { + fn test_size_success() { let mut ctx = Context::new(); - // Test with wrong number of parameters (no parameters) - let values = vec![]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong parameter type - let values = vec![create_string_value("not_an_object")]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![create_test_object(), create_string_value("extra")]; - let result = size(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); + // non-empty object + let mut run = dummy_run; + let args = vec![a_struct(s_test())]; + let signal = size(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::NumberValue(n)) => assert_eq!(n, 3.0), _ => panic!("Expected NumberValue") } + + // empty object + let mut run = dummy_run; + let args = vec![a_struct(s_empty())]; + let signal = size(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::NumberValue(n)) => assert_eq!(n, 0.0), _ => panic!("Expected NumberValue") } } #[test] fn test_keys_success() { let mut ctx = Context::new(); - // Test with object containing fields - let test_object = create_test_object(); - let values = vec![test_object]; - let signal = keys(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { + // with fields + let mut run = dummy_run; + let args = vec![a_struct(s_test())]; + let signal = keys(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::ListValue(list)) => { - assert_eq!(list.values.len(), 3); - - // Convert to strings to check if all expected keys are present - let mut key_strings: Vec = list - .values - .iter() - .filter_map(|v| match &v.kind { - Some(Kind::StringValue(s)) => Some(s.clone()), - _ => None, - }) - .collect(); - key_strings.sort(); - - let mut expected = - vec!["active".to_string(), "age".to_string(), "name".to_string()]; - expected.sort(); + let mut got: Vec = list.values.iter().filter_map(|v| { + if let Some(Kind::StringValue(s)) = &v.kind { Some(s.clone()) } else { None } + }).collect(); + got.sort(); - assert_eq!(key_strings, expected); + let mut expected = vec!["active".to_string(), "age".to_string(), "name".to_string()]; + expected.sort(); + assert_eq!(got, expected); } _ => panic!("Expected ListValue"), } - // Test with empty object - let empty_object = create_empty_object(); - let values = vec![empty_object]; - let signal = keys(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { + // empty object => empty list + let mut run = dummy_run; + let args = vec![a_struct(s_empty())]; + let signal = keys(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { Some(Kind::ListValue(list)) => assert_eq!(list.values.len(), 0), _ => panic!("Expected ListValue"), } } #[test] - fn test_keys_runtime_exception() { + fn test_set_success_and_overwrite() { let mut ctx = Context::new(); - // Test with wrong number of parameters - let values = vec![]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong parameter type - let values = vec![create_number_value(42.0)]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid value - let values = vec![create_invalid_value()]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![create_test_object(), create_string_value("extra")]; - let result = keys(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_set_success() { - let mut ctx = Context::new(); - - // Test setting a new key - let test_object = create_test_object(); - let values = vec![ - test_object.clone(), - create_string_value("email"), - create_string_value("john@example.com"), - ]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), 4); // original 3 + 1 new - assert!(struct_val.fields.contains_key("email")); - - match struct_val.fields.get("email") { - Some(Value { - kind: Some(Kind::StringValue(email)), - }) => { - assert_eq!(email, "john@example.com"); - } - _ => panic!("Expected email field to be a string"), + // set new key + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("email"), Argument::Eval(v_string("john@example.com"))]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { + Some(Kind::StructValue(st)) => { + assert_eq!(st.fields.len(), 4); + match st.fields.get("email") { + Some(Value { kind: Some(Kind::StringValue(s)), .. }) => assert_eq!(s, "john@example.com"), + _ => panic!("Expected email to be a string"), } } _ => panic!("Expected StructValue"), } - // Test overwriting an existing key - let values = vec![ - test_object, - create_string_value("age"), - create_number_value(31.0), - ]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), 3); // same number of fields - - match struct_val.fields.get("age") { - Some(Value { - kind: Some(Kind::NumberValue(age)), - }) => { - assert_eq!(*age, 31.0); - } - _ => panic!("Expected age field to be a number"), + // overwrite existing key + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("age"), Argument::Eval(v_number(31.0))]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { + Some(Kind::StructValue(st)) => { + assert_eq!(st.fields.len(), 3); + match st.fields.get("age") { + Some(Value { kind: Some(Kind::NumberValue(n)), .. }) => assert_eq!(*n, 31.0), + _ => panic!("Expected age to be a number"), } } _ => panic!("Expected StructValue"), @@ -464,69 +227,40 @@ mod tests { } #[test] - fn test_set_with_empty_object() { + fn test_set_with_empty_object_and_nested() { let mut ctx = Context::new(); - let empty_object = create_empty_object(); - - let values = vec![ - empty_object, - create_string_value("first_key"), - create_bool_value(true), - ]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), 1); - assert!(struct_val.fields.contains_key("first_key")); - - match struct_val.fields.get("first_key") { - Some(Value { - kind: Some(Kind::BoolValue(val)), - }) => { - assert_eq!(*val, true); - } - _ => panic!("Expected first_key field to be a boolean"), + // empty object -> add first key + let mut run = dummy_run; + let args = vec![a_struct(s_empty()), a_string("first_key"), Argument::Eval(v_bool(true))]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { + Some(Kind::StructValue(st)) => { + assert_eq!(st.fields.len(), 1); + match st.fields.get("first_key") { + Some(Value { kind: Some(Kind::BoolValue(b)), .. }) => assert_eq!(*b, true), + _ => panic!("Expected first_key to be a bool"), } } _ => panic!("Expected StructValue"), } - } - #[test] - fn test_set_with_different_value_types() { - let mut ctx = Context::new(); - let test_object = create_test_object(); - - // Test setting with a nested object - let mut nested_fields = HashMap::new(); - nested_fields.insert("street".to_string(), create_string_value("123 Main St")); - let nested_object = create_object_value(nested_fields); - - let values = vec![test_object, create_string_value("address"), nested_object]; - let signal = set(&values, &mut ctx); - - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), + // nested object value + let nested = { + let mut nf = HashMap::new(); + nf.insert("street".to_string(), v_string("123 Main St")); + v_struct(nf) }; - - match result.kind { - Some(Kind::StructValue(struct_val)) => { - assert!(struct_val.fields.contains_key("address")); - - match struct_val.fields.get("address") { - Some(Value { - kind: Some(Kind::StructValue(_)), - }) => { - // Successfully set nested object - } - _ => panic!("Expected address field to be a struct"), + let mut run = dummy_run; + let args = vec![a_struct(s_test()), a_string("address"), Argument::Eval(nested)]; + let signal = set(&args, &mut ctx, &mut run); + let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + match v.kind { + Some(Kind::StructValue(st)) => { + match st.fields.get("address") { + Some(Value { kind: Some(Kind::StructValue(_)), .. }) => { /* ok */ } + _ => panic!("Expected address to be a struct"), } } _ => panic!("Expected StructValue"), @@ -534,101 +268,20 @@ mod tests { } #[test] - fn test_set_runtime_exception() { + fn test_set_preserves_original_struct() { let mut ctx = Context::new(); + let original = s_test(); + let original_len = original.fields.len(); - // Test with wrong number of parameters (too few) - let values = vec![create_test_object(), create_string_value("key")]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong first parameter type (not an object) - let values = vec![ - create_string_value("not_an_object"), - create_string_value("key"), - create_string_value("value"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with wrong second parameter type (not a string key) - let values = vec![ - create_test_object(), - create_number_value(123.0), - create_string_value("value"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with invalid values - let values = vec![ - create_invalid_value(), - create_string_value("key"), - create_string_value("value"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with no parameters - let values = vec![]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - - // Test with too many parameters - let values = vec![ - create_test_object(), - create_string_value("key"), - create_string_value("value"), - create_string_value("extra"), - ]; - let result = set(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_set_preserves_original_object() { - let mut ctx = Context::new(); - let original_object = create_test_object(); - - // Get the original size - let original_size = match &original_object.kind { - Some(Kind::StructValue(struct_val)) => struct_val.fields.len(), - _ => panic!("Expected StructValue"), - }; - - let values = vec![ - original_object.clone(), - create_string_value("new_key"), - create_string_value("new_value"), - ]; - let signal = set(&values, &mut ctx); - - let _value = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - // Verify original object is unchanged - match &original_object.kind { - Some(Kind::StructValue(struct_val)) => { - assert_eq!(struct_val.fields.len(), original_size); - assert!(!struct_val.fields.contains_key("new_key")); - } - _ => panic!("Expected StructValue"), - } - } - - #[test] - fn test_function_name_mapping() { - // Test that the function names in collect_object_functions match expected patterns - let functions = collect_object_functions(); + // keep a clone to assert immutability + let orig_clone = original.clone(); - assert_eq!(functions.len(), 4); + let mut run = dummy_run; + let args = vec![a_struct(original), a_string("new_key"), Argument::Eval(v_string("new_val"))]; + let _ = set(&args, &mut ctx, &mut run); - let function_names: Vec<&str> = functions.iter().map(|(name, _)| *name).collect(); - assert!(function_names.contains(&"std::object::contains_key")); - assert!(function_names.contains(&"std::object::keys")); - assert!(function_names.contains(&"std::object::size")); - assert!(function_names.contains(&"std::object::set")); + // ensure original (captured clone) unchanged + assert_eq!(orig_clone.fields.len(), original_len); + assert!(!orig_clone.fields.contains_key("new_key")); } } diff --git a/taurus/src/implementation/text.rs b/taurus/src/implementation/text.rs index 7ba2f54..93a9376 100644 --- a/taurus/src/implementation/text.rs +++ b/taurus/src/implementation/text.rs @@ -1,61 +1,61 @@ +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError, }; +use crate::{context::Context, error::RuntimeError}; use base64::Engine; use tucana::shared::{ListValue, Value, value::Kind}; -pub fn collect_text_functions() -> Vec<(&'static str, crate::reg)> { +pub fn collect_text_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { 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), + ("std::text::as_bytes", HandlerFn::eager(as_bytes, 1)), + ("std::text::byte_size", HandlerFn::eager(byte_size, 1)), + ("std::text::capitalize", HandlerFn::eager(capitalize, 1)), + ("std::text::lowercase", HandlerFn::eager(lowercase, 1)), + ("std::text::uppercase", HandlerFn::eager(uppercase, 1)), + ("std::text::swapcase", HandlerFn::eager(swapcase, 1)), + ("std::text::trim", HandlerFn::eager(trim, 1)), + ("std::text::chars", HandlerFn::eager(chars, 1)), + ("std::text::at", HandlerFn::eager(at, 2)), + ("std::text::append", HandlerFn::eager(append, 2)), + ("std::text::prepend", HandlerFn::eager(prepend, 2)), + ("std::text::insert", HandlerFn::eager(insert, 3)), + ("std::text::length", HandlerFn::eager(length, 1)), + ("std::text::reverse", HandlerFn::eager(reverse, 1)), + ("std::text::remove", HandlerFn::eager(remove, 3)), + ("std::text::replace", HandlerFn::eager(replace, 3)), + ("std::text::replace_first", HandlerFn::eager(replace_first, 3)), + ("std::text::replace_last", HandlerFn::eager(replace_last, 3)), + ("std::text::hex", HandlerFn::eager(hex,1)), + ("std::text::octal", HandlerFn::eager(octal,1 )), + ("std::text::index_of", HandlerFn::eager(index_of, 2)), + ("std::text::contains", HandlerFn::eager(contains, 2)), + ("std::text::split", HandlerFn::eager(split, 2)), + ("std::text::starts_with", HandlerFn::eager(starts_with, 2)), + ("std::text::ends_with", HandlerFn::eager(ends_with, 2)), + ("std::text::to_ascii", HandlerFn::eager(to_ascii, 1)), + ("std::text::from_ascii", HandlerFn::eager(from_ascii, 1)), + ("std::text::encode", HandlerFn::eager(encode, 2)), + ("std::text::decode", HandlerFn::eager(decode, 2)), + ("std::text::is_equal", HandlerFn::eager(is_equal, 2)), ] } -fn as_bytes(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected one string as argument but received {:?}", values), - )); - }; +fn arg_err>(msg: S) -> Signal { + Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + msg.into(), + )) +} + +fn as_bytes(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); let bytes: Vec = value .as_bytes() .iter() - .map(|byte| Value { - kind: Some(Kind::NumberValue(*byte as f64)), + .map(|b| Value { + kind: Some(Kind::NumberValue(*b as f64)), }) .collect(); @@ -64,128 +64,56 @@ fn as_bytes(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn byte_size(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - +fn byte_size(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); Signal::Success(Value { kind: Some(Kind::NumberValue(value.len() as f64)), }) } -fn capitalize(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn capitalize( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String); - let capitalized_value = value - .split(" ") + let capitalized = value + .split(' ') .map(|word| { if word.is_empty() { return String::from(word); } - - if word.len() == 1 { - return word.to_uppercase(); - } - - let first = word.chars().next(); - - if first.is_some() { - let first = first.unwrap(); - String::from(first).to_uppercase() + &word[1..] - } else { - String::from(word) + let mut chars = word.chars(); + match chars.next() { + Some(first) => first.to_uppercase().collect::() + chars.as_str(), + None => String::from(word), } }) .collect::>() .join(" "); Signal::Success(Value { - kind: Some(Kind::StringValue(capitalized_value)), + kind: Some(Kind::StringValue(capitalized)), }) } -fn uppercase(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - +fn uppercase(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); Signal::Success(Value { kind: Some(Kind::StringValue(value.to_uppercase())), }) } -fn lowercase(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - +fn lowercase(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); Signal::Success(Value { kind: Some(Kind::StringValue(value.to_lowercase())), }) } -fn swapcase(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn swapcase(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); let swapped = value .chars() @@ -198,30 +126,24 @@ fn swapcase(values: &[Value], _ctx: &mut Context) -> Signal { c.to_string() } }) - .collect(); + .collect::(); Signal::Success(Value { kind: Some(Kind::StringValue(swapped)), }) } -fn chars(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn trim(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); + Signal::Success(Value { + kind: Some(Kind::StringValue(value.trim().to_string())), + }) +} + +fn chars(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); - let chars = value + let list = value .chars() .map(|c| Value { kind: Some(Kind::StringValue(c.to_string())), @@ -229,43 +151,19 @@ fn chars(values: &[Value], _ctx: &mut Context) -> Signal { .collect::>(); Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { values: chars })), + kind: Some(Kind::ListValue(ListValue { values: list })), }) } -fn at(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(index)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string followed by one number as arguments but received {:?}", - values - ), - )); - }; +fn at(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, index: f64); - if index < &0.0 { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected a positive number as the second argument but received {}", - index - ), - )); + if index < 0.0 { + return arg_err("Expected a non-negative index"); } - let usize_index = *index as usize; - let char = value.chars().nth(usize_index); - - match char { + let idx = index as usize; + match value.chars().nth(idx) { Some(c) => Signal::Success(Value { kind: Some(Kind::StringValue(c.to_string())), }), @@ -274,158 +172,84 @@ fn at(values: &[Value], _ctx: &mut Context) -> Signal { format!( "Index {} is out of bounds for string of length {}", index, - value.len() + value.chars().count() ), )), } } -fn trim(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an arguments but received {:?}", - values - ), - )); - }; - +fn append(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, suffix: String); Signal::Success(Value { - kind: Some(Kind::StringValue(value.trim().to_string())), + kind: Some(Kind::StringValue(value + &suffix)), }) } -fn append(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(suffix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected two numbers as argument but received {:?}", values), - )); - }; - +fn prepend(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, prefix: String); Signal::Success(Value { - kind: Some(Kind::StringValue(value.clone() + suffix)), + kind: Some(Kind::StringValue(prefix + &value)), }) } -fn prepend(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(prefix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; +fn insert(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, position: f64, text: String); - Signal::Success(Value { - kind: Some(Kind::StringValue(prefix.clone() + value)), - }) -} + if position < 0.0 { + return arg_err("Expected a non-negative position"); + } -fn insert(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(position)), - }, - Value { - kind: Some(Kind::StringValue(text)), - }, - ] = values - else { + let pos = position as usize; + // Byte-wise position (consistent with previous behavior). For char-wise, compute byte index from char idx. + if pos > value.len() { return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected string, number, and string as arguments but received {:?}", - values - ), + "IndexOutOfBoundsRuntimeError", + format!("Position {} exceeds byte length {}", pos, value.len()), )); - }; + } - let usize_position = *position as usize; - let mut new_value = value.clone(); - new_value.insert_str(usize_position, text.as_str()); + let mut new_value = value; + new_value.insert_str(pos, &text); Signal::Success(Value { kind: Some(Kind::StringValue(new_value)), }) } -fn length(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; - +fn length(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); Signal::Success(Value { kind: Some(Kind::NumberValue(value.chars().count() as f64)), }) } -fn remove(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::NumberValue(from)), - }, - Value { - kind: Some(Kind::NumberValue(to)), - }, - ] = values - else { +fn remove(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, from: f64, to: f64); + + if from < 0.0 || to < 0.0 { + return arg_err("Expected non-negative indices"); + } + + let from_u = from as usize; + let to_u = to as usize; + + let chars = value.chars().collect::>(); + if from_u > chars.len() || to_u > chars.len() { return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", + "IndexOutOfBoundsRuntimeError", format!( - "Expected one string followed by two numbers as arguments but received {:?}", - values + "Indices [{}, {}) out of bounds for length {}", + from_u, + to_u, + chars.len() ), )); - }; - - let chars = value.chars().collect::>(); + } let new = chars .into_iter() .enumerate() - .filter(|&(i, _)| i < *from as usize || i >= *to as usize) + .filter(|&(i, _)| i < from_u || i >= to_u) .map(|e| e.1) .collect::(); @@ -434,87 +258,34 @@ fn remove(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn replace(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(old)), - }, - Value { - kind: Some(Kind::StringValue(new)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three strings as arguments but received {:?}", - values - ), - )); - }; - - let replaced = value.replace(old, new); - +fn replace(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, old: String, new: String); + let replaced = value.replace(&old, &new); Signal::Success(Value { kind: Some(Kind::StringValue(replaced)), }) } -fn replace_first(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(old)), - }, - Value { - kind: Some(Kind::StringValue(new)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three strings as arguments but received {:?}", - values - ), - )); - }; - - let replaced = value.replacen(old, new, 1); - +fn replace_first( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String, old: String, new: String); + let replaced = value.replacen(&old, &new, 1); Signal::Success(Value { kind: Some(Kind::StringValue(replaced)), }) } -fn replace_last(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(old)), - }, - Value { - kind: Some(Kind::StringValue(new)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected three strings as arguments but received {:?}", - values - ), - )); - }; +fn replace_last( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String, old: String, new: String); - fn replace_last(haystack: &str, needle: &str, replacement: &str) -> String { + fn replace_last_impl(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()); @@ -523,37 +294,23 @@ fn replace_last(values: &[Value], _ctx: &mut Context) -> Signal { result.push_str(&haystack[pos + needle.len()..]); result } else { - haystack.to_string() // kein Vorkommen gefunden, original zurückgeben + haystack.to_string() } } - let replaced = replace_last(value.as_str(), old.as_str(), new.as_str()); - + let replaced = replace_last_impl(&value, &old, &new); Signal::Success(Value { kind: Some(Kind::StringValue(replaced)), }) } -fn hex(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn hex(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); let hex = value .as_bytes() .iter() - .map(|byte| format!("{:02x}", byte)) + .map(|b| format!("{:02x}", b)) .collect::(); Signal::Success(Value { @@ -561,57 +318,26 @@ fn hex(values: &[Value], _ctx: &mut Context) -> Signal { }) } -fn octal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn octal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); - let hex = value + let oct = value .as_bytes() .iter() - .map(|byte| format!("{:03o}", byte)) + .map(|b| format!("{:03o}", b)) .collect::(); Signal::Success(Value { - kind: Some(Kind::StringValue(hex)), + kind: Some(Kind::StringValue(oct)), }) } -fn index_of(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(sub_string)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - - let index_option = value.find(sub_string); +fn index_of(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, sub: String); - match index_option { - Some(index) => Signal::Success(Value { - kind: Some(Kind::NumberValue(index as f64)), + match value.find(&sub) { + Some(idx) => Signal::Success(Value { + kind: Some(Kind::NumberValue(idx as f64)), }), None => Signal::Success(Value { kind: Some(Kind::NumberValue(-1.0)), @@ -619,149 +345,59 @@ fn index_of(values: &[Value], _ctx: &mut Context) -> Signal { } } -fn contains(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(sub_string)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - +fn contains(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, sub: String); Signal::Success(Value { - kind: Some(Kind::BoolValue(value.contains(sub_string))), + kind: Some(Kind::BoolValue(value.contains(&sub))), }) } -fn split(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(delimiter)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; +fn split(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, delimiter: String); - let words = value - .split(delimiter) - .map(|word| Value { - kind: Some(Kind::StringValue(word.to_string())), + let parts = value + .split(&delimiter) + .map(|s| Value { + kind: Some(Kind::StringValue(s.to_string())), }) .collect::>(); Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { values: words })), + kind: Some(Kind::ListValue(ListValue { values: parts })), }) } -fn reverse(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn reverse(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); let reversed = value.chars().rev().collect::(); - Signal::Success(Value { kind: Some(Kind::StringValue(reversed)), }) } -fn starts_with(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(prefix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - +fn starts_with( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + args!(args => value: String, prefix: String); Signal::Success(Value { - kind: Some(Kind::BoolValue(value.starts_with(prefix))), + kind: Some(Kind::BoolValue(value.starts_with(&prefix))), }) } -fn ends_with(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(suffix)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - +fn ends_with(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, suffix: String); Signal::Success(Value { - kind: Some(Kind::BoolValue(value.ends_with(suffix))), + kind: Some(Kind::BoolValue(value.ends_with(&suffix))), }) } -fn to_ascii(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected one string as an argument but received {:?}", - values - ), - )); - }; +fn to_ascii(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String); - let ascii_value = value + let ascii = value .bytes() .map(|b| Value { kind: Some(Kind::NumberValue(b as f64)), @@ -769,121 +405,64 @@ fn to_ascii(values: &[Value], _ctx: &mut Context) -> Signal { .collect::>(); Signal::Success(Value { - kind: Some(Kind::ListValue(ListValue { - values: ascii_value, - })), + kind: Some(Kind::ListValue(ListValue { values: ascii })), }) } -fn from_ascii(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::ListValue(list)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected a list of numbers as an argument but received {:?}", - values - ), - )); - }; +fn from_ascii( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { + // Requires a TryFromArg impl for ListValue in your macro system. + args!(args => list: ListValue); let string = list .values .iter() - .map(|number_value| { - if let Value { - kind: Some(Kind::NumberValue(number)), - } = number_value - { - if (&0.0..=&127.0).contains(&number) { - Some(*number as u8 as char) - } else { - None - } - } else { - None - } + .map(|v| match v { + Value { + kind: Some(Kind::NumberValue(n)), + } if *n >= 0.0 && *n <= 127.0 => Some(*n as u8 as char), + _ => None, }) .collect::>(); match string { - Some(string) => Signal::Success(Value { - kind: Some(Kind::StringValue(string)), + Some(s) => Signal::Success(Value { + kind: Some(Kind::StringValue(s)), }), - None => Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - "Expected a list of numbers between 0 and 127".to_string(), - )), + None => arg_err("Expected a list of numbers between 0 and 127"), } } -//TODO: Implement encode function , what about decode? UTF-8, 16 and 32 does not make sense - -fn encode(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(encoding)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; +// NOTE: "encode"/"decode" currently only support base64. +fn encode(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, encoding: String); - let encoded_string = match encoding.clone().to_lowercase().as_str() { + let encoded = match encoding.to_lowercase().as_str() { "base64" => base64::prelude::BASE64_STANDARD.encode(value), _ => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Unsupported encoding: {}", encoding), - )); + return arg_err(format!("Unsupported encoding: {}", encoding)); } }; Signal::Success(Value { - kind: Some(Kind::StringValue(encoded_string)), + kind: Some(Kind::StringValue(encoded)), }) } -fn decode(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(value)), - }, - Value { - kind: Some(Kind::StringValue(encoding)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; +fn decode(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => value: String, encoding: String); - let decoded_string = match encoding.clone().to_lowercase().as_str() { + let decoded = match encoding.to_lowercase().as_str() { "base64" => match base64::prelude::BASE64_STANDARD.decode(value) { Ok(bytes) => match String::from_utf8(bytes) { - Ok(string) => string, + Ok(s) => s, Err(err) => { return Signal::Failure(RuntimeError::simple( "DecodeError", - format!("Failed to decode base64 string: {:?}", err), + format!("Failed to decode base64 bytes to UTF-8: {:?}", err), )); } }, @@ -894,38 +473,16 @@ fn decode(values: &[Value], _ctx: &mut Context) -> Signal { )); } }, - _ => { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Unsupported decoding: {}", encoding), - )); - } + _ => return arg_err(format!("Unsupported decoding: {}", encoding)), }; Signal::Success(Value { - kind: Some(Kind::StringValue(decoded_string)), + kind: Some(Kind::StringValue(decoded)), }) } -fn is_equal(values: &[Value], _ctx: &mut Context) -> Signal { - let [ - Value { - kind: Some(Kind::StringValue(lhs)), - }, - Value { - kind: Some(Kind::StringValue(rhs)), - }, - ] = values - else { - return Signal::Failure(RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!( - "Expected two strings as arguments but received {:?}", - values - ), - )); - }; - +fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { + args!(args => lhs: String, rhs: String); Signal::Success(Value { kind: Some(Kind::BoolValue(lhs == rhs)), }) @@ -937,1293 +494,259 @@ mod tests { use crate::context::Context; use tucana::shared::{ListValue, Value, value::Kind}; - // Helper function to create a string value - fn create_string_value(s: &str) -> Value { - Value { - kind: Some(Kind::StringValue(s.to_string())), - } + // ---------- helpers: build Arguments ---------- + fn a_str(s: &str) -> Argument { + Argument:: Eval(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)), - } + fn a_num(n: f64) -> Argument { + Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) } - - // Helper function to create a bool value - fn create_bool_value(b: bool) -> Value { - Value { - kind: Some(Kind::BoolValue(b)), - } + fn a_list(vals: Vec) -> Argument { + Argument::Eval(Value { kind: Some(Kind::ListValue(ListValue { values: vals })) }) } - // Helper function to create a list value - fn create_list_value(values: Vec) -> Value { - Value { - kind: Some(Kind::ListValue(ListValue { values })), - } + // ---------- helpers: build bare Values ---------- + fn v_str(s: &str) -> Value { + Value { kind: Some(Kind::StringValue(s.to_string())) } } - - #[test] - fn test_as_bytes_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = as_bytes(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - 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"); - } + fn v_num(n: f64) -> Value { + Value { kind: Some(Kind::NumberValue(n)) } } - #[test] - fn test_as_bytes_empty_string() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = as_bytes(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 0); - } else { - panic!("Expected ListValue"); + // ---------- helpers: extract from Signal ---------- + fn expect_num(sig: Signal) -> f64 { + match sig { + Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + other => panic!("Expected NumberValue, got {:?}", other), } } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_byte_size_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = byte_size(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = byte_size(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = byte_size(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = capitalize(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = capitalize(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = capitalize(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = uppercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = uppercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = lowercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = lowercase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = swapcase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = swapcase(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = chars(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - 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"); + fn expect_bool(sig: Signal) -> bool { + match sig { + Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + other => panic!("Expected BoolValue, got {:?}", other), } } - - #[test] - fn test_chars_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = chars(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - if let Value { - kind: Some(Kind::ListValue(list)), - } = result - { - assert_eq!(list.values.len(), 0); - } else { - panic!("Expected ListValue"); + fn expect_str(sig: Signal) -> String { + match sig { + Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + other => panic!("Expected StringValue, got {:?}", other), } } - - #[test] - fn test_at_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_number_value(1.0)]; - let signal = at(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = at(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_trim_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value(" hello world ")]; - let signal = trim(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = trim(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = append(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = append(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = prepend(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = prepend(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = insert(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = insert(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = insert(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = length(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = length(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(0.0)); + fn expect_list(sig: Signal) -> Vec { + match sig { + Signal::Success(Value { kind: Some(Kind::ListValue(ListValue { values })) }) => values, + other => panic!("Expected ListValue, got {:?}", other), + } } - #[test] - fn test_length_unicode() { - let mut ctx = Context::new(); - let values = vec![create_string_value("café")]; - let signal = length(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(4.0)); // 4 characters + // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` + fn dummy_run(_: i64) -> Signal { + Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) } - #[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 signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("heorld")); - } + // ---------- tests ---------- #[test] - fn test_remove_from_beginning() { + fn test_as_bytes_and_byte_size() { let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello"), - create_number_value(0.0), - create_number_value(2.0), - ]; - let signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("llo")); - } + let mut run = dummy_run; - #[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 signal = replace(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hi world hi")); - } + // "hello" -> 5 bytes + let bytes = expect_list(as_bytes(&[a_str("hello")], &mut ctx, &mut run)); + assert_eq!(bytes.len(), 5); + assert_eq!(bytes[0], v_num(104.0)); // 'h' - #[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 signal = replace(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } + let mut run = dummy_run; + assert_eq!(expect_num(byte_size(&[a_str("hello")], &mut ctx, &mut run)), 5.0); - #[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 signal = replace_first(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hi world hello")); + // unicode: "café" -> 5 bytes, 4 chars + let mut run = dummy_run; + assert_eq!(expect_num(byte_size(&[a_str("café")], &mut ctx, &mut run)), 5.0); + let mut run = dummy_run; + assert_eq!(expect_num(length(&[a_str("café")], &mut ctx, &mut run)), 4.0); } #[test] - fn test_replace_last_valid() { + fn test_case_ops_and_trim() { let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world hello"), - create_string_value("hello"), - create_string_value("hi"), - ]; - let signal = replace_last(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = replace_last(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("hello world")); - } + let mut run = dummy_run; + assert_eq!(expect_str(capitalize(&[a_str("hello world")], &mut ctx, &mut run)), "Hello World"); - #[test] - fn test_hex_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = hex(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("68656c6c6f")); - } + let mut run = dummy_run; + assert_eq!(expect_str(uppercase(&[a_str("Hello")], &mut ctx, &mut run)), "HELLO"); - #[test] - fn test_hex_empty() { - let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = hex(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } + let mut run = dummy_run; + assert_eq!(expect_str(lowercase(&[a_str("Hello")], &mut ctx, &mut run)), "hello"); - #[test] - fn test_octal_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("A")]; - let signal = octal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("101")); // 'A' is 65 in ASCII, 101 in octal - } + let mut run = dummy_run; + assert_eq!(expect_str(swapcase(&[a_str("HeLLo123")], &mut ctx, &mut run)), "hEllO123"); - #[test] - fn test_index_of_found() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("world"), - ]; - let signal = index_of(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(6.0)); + let mut run = dummy_run; + assert_eq!(expect_str(trim(&[a_str(" hi ")], &mut ctx, &mut run)), "hi"); } #[test] - fn test_index_of_not_found() { + fn test_chars_and_at() { let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("xyz"), - ]; - let signal = index_of(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = index_of(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_number_value(0.0)); - } + let mut run = dummy_run; + let chars_list = expect_list(chars(&[a_str("abc")], &mut ctx, &mut run)); + assert_eq!(chars_list, vec![v_str("a"), v_str("b"), v_str("c")]); - #[test] - fn test_contains_true() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("world"), - ]; - let signal = contains(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_bool_value(true)); - } + let mut run = dummy_run; + assert_eq!(expect_str(at(&[a_str("hello"), a_num(1.0)], &mut ctx, &mut run)), "e"); - #[test] - fn test_contains_false() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("xyz"), - ]; - let signal = contains(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = split(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - 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"); + // out-of-bounds + let mut run = dummy_run; + match at(&[a_str("hi"), a_num(5.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure, got {:?}", s), } - } - - #[test] - fn test_split_no_delimiter() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value(",")]; - let signal = split(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - 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"); + // negative + let mut run = dummy_run; + match at(&[a_str("hi"), a_num(-1.0)], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure, got {:?}", s), } } #[test] - fn test_reverse_valid() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello")]; - let signal = reverse(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("olleh")); - } - - #[test] - fn test_reverse_empty() { + fn test_append_prepend_insert_length() { let mut ctx = Context::new(); - let values = vec![create_string_value("")]; - let signal = reverse(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); - } - #[test] - fn test_reverse_palindrome() { - let mut ctx = Context::new(); - let values = vec![create_string_value("aba")]; - let signal = reverse(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("aba")); - } + let mut run = dummy_run; + assert_eq!(expect_str(append(&[a_str("hello"), a_str(" world")], &mut ctx, &mut run)), "hello world"); - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } + let mut run = dummy_run; + assert_eq!(expect_str(prepend(&[a_str("world"), a_str("hello ")], &mut ctx, &mut run)), "hello world"); - #[test] - fn test_encode_correct() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("BASE64")]; - let signal = encode(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; + // insert uses BYTE index; for ASCII this matches char index + let mut run = dummy_run; assert_eq!( - result, - Value { - kind: Some(Kind::StringValue(String::from("aGVsbG8="))) - } + expect_str(insert(&[a_str("hello"), a_num(2.0), a_str("XXX")], &mut ctx, &mut run)), + "heXXXllo" ); - } - #[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_eq!(result, Signal::Failure(RuntimeError::default())); + let mut run = dummy_run; + assert_eq!(expect_num(length(&[a_str("hello")], &mut ctx, &mut run)), 5.0); } #[test] - fn test_decode_invalid_encoding() { + fn test_remove_replace_variants() { let mut ctx = Context::new(); - let values = vec![create_string_value("aba"), create_string_value("gug")]; - let result = decode(&values, &mut ctx); - assert_eq!(result, Signal::Failure(RuntimeError::default())); - } - #[test] - fn test_decode_correct() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("aGVsbG8="), - create_string_value("BASE64"), - ]; - let signal = decode(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; + // remove uses CHAR indices [from, to) + let mut run = dummy_run; assert_eq!( - result, - Value { - kind: Some(Kind::StringValue(String::from("hello"))) - } + expect_str(remove(&[a_str("hello world"), a_num(2.0), a_num(7.0)], &mut ctx, &mut run)), + "heorld" ); - } - - #[test] - fn test_starts_with_true() { - let mut ctx = Context::new(); - let values = vec![ - create_string_value("hello world"), - create_string_value("hello"), - ]; - let signal = starts_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = starts_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = ends_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = ends_with(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = to_ascii(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - - 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 signal = from_ascii(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = from_ascii(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[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_eq!(result, Signal::Failure(RuntimeError::default())); - } - - #[test] - fn test_is_equal_true() { - let mut ctx = Context::new(); - let values = vec![create_string_value("hello"), create_string_value("hello")]; - let signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = is_equal(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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_eq!( - as_bytes(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - byte_size(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - capitalize(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - uppercase(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - lowercase(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - swapcase(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - chars(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); + let mut run = dummy_run; assert_eq!( - trim(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - length(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - reverse(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - hex(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) + expect_str(replace(&[a_str("hello world hello"), a_str("hello"), a_str("hi")], &mut ctx, &mut run)), + "hi world hi" ); + + let mut run = dummy_run; assert_eq!( - octal(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) + expect_str(replace_first(&[a_str("one two one"), a_str("one"), a_str("1")], &mut ctx, &mut run)), + "1 two one" ); + + let mut run = dummy_run; assert_eq!( - to_ascii(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) + expect_str(replace_last(&[a_str("one two one"), a_str("one"), a_str("1")], &mut ctx, &mut run)), + "one two 1" ); } #[test] - fn test_invalid_arguments_two_strings() { + fn test_hex_octal_reverse() { 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)]; + let mut run = dummy_run; + assert_eq!(expect_str(hex(&[a_str("hello")], &mut ctx, &mut run)), "68656c6c6f"); - assert_eq!( - append(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - prepend(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - index_of(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - contains(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - split(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - starts_with(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - ends_with(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - is_equal(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); + let mut run = dummy_run; + assert_eq!(expect_str(octal(&[a_str("A")], &mut ctx, &mut run)), "101"); + + let mut run = dummy_run; + assert_eq!(expect_str(reverse(&[a_str("hello")], &mut ctx, &mut run)), "olleh"); } #[test] - fn test_invalid_arguments_string_and_number() { + fn test_index_contains_split_starts_ends() { 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")]; + let mut run = dummy_run; + assert_eq!(expect_num(index_of(&[a_str("hello world"), a_str("world")], &mut ctx, &mut run)), 6.0); - assert_eq!( - at(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - } + let mut run = dummy_run; + assert_eq!(expect_num(index_of(&[a_str("hello"), a_str("xyz")], &mut ctx, &mut run)), -1.0); - #[test] - fn test_invalid_arguments_three_params() { - let mut ctx = Context::new(); + let mut run = dummy_run; + assert!(expect_bool(contains(&[a_str("hello world"), a_str("world")], &mut ctx, &mut run))); - // Test functions that expect 3 arguments - let invalid_values = vec![ - create_string_value("hello"), - create_string_value("test"), - create_number_value(123.0), - ]; + let mut run = dummy_run; + let split_list = expect_list(split(&[a_str("a,b,c"), a_str(",")], &mut ctx, &mut run)); + assert_eq!(split_list, vec![v_str("a"), v_str("b"), v_str("c")]); - assert_eq!( - insert(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - remove(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - replace(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - replace_first(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - replace_last(&invalid_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); + let mut run = dummy_run; + assert!(expect_bool(starts_with(&[a_str("hello"), a_str("he")], &mut ctx, &mut run))); + + let mut run = dummy_run; + assert!(expect_bool(ends_with(&[a_str("hello"), a_str("lo")], &mut ctx, &mut run))); } #[test] - fn test_wrong_argument_count() { + fn test_to_ascii_and_from_ascii() { 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"), - ]; + let mut run = dummy_run; + let ascii_vals = expect_list(to_ascii(&[a_str("AB")], &mut ctx, &mut run)); + assert_eq!(ascii_vals, vec![v_num(65.0), v_num(66.0)]); - assert_eq!( - as_bytes(&empty_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - as_bytes(&too_many_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); + let mut run = dummy_run; + let list_arg = a_list(vec![v_num(65.0), v_num(66.0), v_num(67.0)]); + assert_eq!(expect_str(from_ascii(&[list_arg], &mut ctx, &mut run)), "ABC"); - assert_eq!( - append(&empty_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); - assert_eq!( - append(&too_many_values, &mut ctx), - Signal::Failure(RuntimeError::default()) - ); + // invalid element + let mut run = dummy_run; + let list_arg = a_list(vec![v_num(65.0), v_num(128.0)]); + match from_ascii(&[list_arg], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for invalid ASCII, got {:?}", s), + } } #[test] - fn test_edge_cases() { + fn test_encode_decode_base64_and_is_equal() { 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_eq!(length(&values, &mut ctx), Signal::Success(Value::default())); + let mut run = dummy_run; assert_eq!( - reverse(&values, &mut ctx), - Signal::Success(Value::default()) + expect_str(encode(&[a_str("hello"), a_str("BASE64")], &mut ctx, &mut run)), + "aGVsbG8=" ); - assert_eq!( - uppercase(&values, &mut ctx), - Signal::Success(Value::default()) - ); - - // Test with special characters - let special_string = "!@#$%^&*(){}[]|\\:;\"'<>,.?/~`"; - let values = vec![create_string_value(special_string)]; - assert_eq!(length(&values, &mut ctx), Signal::Success(Value::default())); + let mut run = dummy_run; assert_eq!( - uppercase(&values, &mut ctx), - Signal::Success(Value::default()) + expect_str(decode(&[a_str("aGVsbG8="), a_str("base64")], &mut ctx, &mut run)), + "hello" ); - assert_eq!( - reverse(&values, &mut ctx), - Signal::Success(Value::default()) - ); - - // Test with unicode characters - let unicode_string = "🦀🚀✨🎉"; - let values = vec![create_string_value(unicode_string)]; - - assert_eq!(length(&values, &mut ctx), Signal::Success(Value::default())); - assert_eq!( - reverse(&values, &mut ctx), - Signal::Success(Value::default()) - ); - assert_eq!(chars(&values, &mut ctx), Signal::Success(Value::default())); - } - - #[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_eq!(result, Signal::Success(Value::default())); + // unsupported codec + let mut run = dummy_run; + match encode(&[a_str("data"), a_str("gug")], &mut ctx, &mut run) { + Signal::Failure(_) => {} + s => panic!("Expected Failure for unsupported encoding, got {:?}", s), } - // 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 signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - 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 signal = remove(&values, &mut ctx); - let result = match signal { - Signal::Success(v) => v, - _ => panic!("Expected Success!"), - }; - assert_eq!(result, create_string_value("")); + let mut run = dummy_run; + assert!(expect_bool(is_equal(&[a_str("x"), a_str("x")], &mut ctx, &mut run))); + let mut run = dummy_run; + assert!(!expect_bool(is_equal(&[a_str("x"), a_str("y")], &mut ctx, &mut run))); } } From 4a650dbc135293ca24f5afa169813a00696f8807 Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sun, 9 Nov 2025 19:20:29 +0100 Subject: [PATCH 05/10] feat: added macro to automatically match input values for a specific pattern --- taurus/src/context/macros.rs | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 taurus/src/context/macros.rs diff --git a/taurus/src/context/macros.rs b/taurus/src/context/macros.rs new file mode 100644 index 0000000..2b17b99 --- /dev/null +++ b/taurus/src/context/macros.rs @@ -0,0 +1,45 @@ +/// Pulls typed parameters from a slice of `Argument` using your `TryFromArgument` +/// impls. Fails early with your `Signal::Failure(RuntimeError::simple(...))`. +macro_rules! args { + ($args_ident:ident => $( $name:ident : $ty:ty ),+ $(,)?) => { + // Arity check + let __expected: usize = 0usize $(+ { let _ = ::core::any::type_name::<$ty>(); 1usize })*; + if $args_ident.len() != __expected { + return $crate::context::signal::Signal::Failure( + $crate::error::RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected {__expected} args but received {}", $args_ident.len()), + ) + ); + } + + // Typed extraction + let mut __i: usize = 0; + $( + let $name: $ty = match < + $ty as $crate::context::argument::TryFromArgument + >::try_from_argument(& $args_ident[__i]) { + Ok(v) => v, + Err(sig) => return sig, + }; + __i += 1; + )+ + }; +} + +/// Asserts there are no arguments. +macro_rules! no_args { + ($args_ident:ident) => { + if !$args_ident.is_empty() { + return $crate::context::signal::Signal::Failure( + $crate::error::RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected 0 args but received {}", $args_ident.len()), + ) + ); + } + }; +} + +pub(crate) use args; +pub(crate) use no_args; \ No newline at end of file From b8b42a9ea8df4eaa13a59505918a555b8c14518a Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sun, 9 Nov 2025 20:01:22 +0100 Subject: [PATCH 06/10] fix: introduced refcell to handle borrow problems --- taurus/src/context/executor.rs | 57 +++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/taurus/src/context/executor.rs b/taurus/src/context/executor.rs index 1fc8728..e77af57 100644 --- a/taurus/src/context/executor.rs +++ b/taurus/src/context/executor.rs @@ -1,19 +1,18 @@ use crate::context::Context; +use crate::context::argument::{Argument, ParameterNode}; +use crate::context::registry::FunctionStore; use crate::context::signal::Signal; use crate::error::RuntimeError; +use std::cell::RefCell; use std::collections::HashMap; -use tucana::shared::{NodeFunction, NodeParameter}; -use crate::context::argument::{Argument, ParameterNode}; -use crate::context::registry::FunctionStore; +use tucana::shared::{NodeFunction}; pub struct Executor<'a> { functions: &'a FunctionStore, nodes: HashMap, - context: Context, + context: RefCell, } -type HandleNodeParameterFn = fn(&mut Executor, node_parameter: &NodeParameter) -> Signal; - impl<'a> Executor<'a> { pub fn new( functions: &'a FunctionStore, @@ -23,11 +22,11 @@ impl<'a> Executor<'a> { Executor { functions, nodes, - context, + context: RefCell::new(context), } } - pub fn execute(&mut self, starting_node_id: i64) -> Signal { + pub fn execute(&self, starting_node_id: i64) -> Signal { let mut current_node_id = starting_node_id; loop { @@ -41,11 +40,13 @@ impl<'a> Executor<'a> { Some(n) => n.clone(), }; - let entry = match self.functions.get(node.runtime_function_id.as_str()) { None => { - return Signal::Failure(RuntimeError::simple_str("FunctionNotFound","The function was not found")) - }, + return Signal::Failure(RuntimeError::simple_str( + "FunctionNotFound", + "The function was not found", + )); + } Some(f) => f, }; @@ -53,11 +54,21 @@ impl<'a> Executor<'a> { for parameter in &node.parameters { let node_value = match ¶meter.value { Some(v) => v, - None => return Signal::Failure(RuntimeError::simple_str("NodeValueNotFound","Missing parameter value")), + None => { + return Signal::Failure(RuntimeError::simple_str( + "NodeValueNotFound", + "Missing parameter value", + )); + } }; let value = match &node_value.value { Some(v) => v, - None => return Signal::Failure(RuntimeError::simple_str("NodeValueNotFound","Missing inner value")), + None => { + return Signal::Failure(RuntimeError::simple_str( + "NodeValueNotFound", + "Missing inner value", + )); + } }; match value { @@ -73,26 +84,28 @@ impl<'a> Executor<'a> { } } - - // Eagerly evaluate args that the function *declares* as Eager for (i, a) in args.iter_mut().enumerate() { - let mode = entry.param_modes.get(i).copied().unwrap_or(ParameterNode::Eager); + let mode = entry + .param_modes + .get(i) + .copied() + .unwrap_or(ParameterNode::Eager); if matches!(mode, ParameterNode::Eager) { if let Argument::Thunk(id) = *a { match self.execute(id) { Signal::Success(v) => *a = Argument::Eval(v), - // propagate control flow immediately - s @ (Signal::Failure(_) | Signal::Return(_) | Signal::Respond(_) | Signal::Stop) => return s, + s @ (Signal::Failure(_) + | Signal::Return(_) + | Signal::Respond(_) + | Signal::Stop) => return s, } } } } - // Provide a runner for Lazy params let mut run = |node_id: i64| self.execute(node_id); - - // Call the handler (no special cases anywhere) - let result = (entry.handler)(&args, &mut self.context, &mut run); + let mut ctx = self.context.borrow_mut(); + let result = (entry.handler)(&args, &mut ctx, &mut run); match result { Signal::Success(value) => { From 54c7bbb51f473449c346684d2e209b0d324a677f Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sun, 9 Nov 2025 20:01:41 +0100 Subject: [PATCH 07/10] ref: cargo fmt --- taurus/src/context/argument.rs | 37 +- taurus/src/context/executor.rs | 2 +- taurus/src/context/macros.rs | 12 +- taurus/src/context/mod.rs | 6 +- taurus/src/implementation/array.rs | 630 +++++++++++++++++++++------ taurus/src/implementation/boolean.rs | 120 +++-- taurus/src/implementation/http.rs | 23 +- taurus/src/implementation/mod.rs | 2 +- taurus/src/implementation/number.rs | 189 ++++++-- taurus/src/implementation/object.rs | 208 +++++++-- taurus/src/implementation/text.rs | 224 ++++++++-- taurus/src/main.rs | 4 +- 12 files changed, 1143 insertions(+), 314 deletions(-) diff --git a/taurus/src/context/argument.rs b/taurus/src/context/argument.rs index f6965c5..7cc6105 100644 --- a/taurus/src/context/argument.rs +++ b/taurus/src/context/argument.rs @@ -1,8 +1,8 @@ -use std::convert::Infallible; -use tucana::shared::{ListValue, Struct, Value}; -use tucana::shared::value::Kind; use crate::context::signal::Signal; use crate::error::RuntimeError; +use std::convert::Infallible; +use tucana::shared::value::Kind; +use tucana::shared::{ListValue, Struct, Value}; #[derive(Clone, Debug)] pub enum Argument { @@ -11,13 +11,13 @@ pub enum Argument { Eval(tucana::shared::Value), // Thunk of NodeFunction identifier // - used for lazy execution of nodes - Thunk(i64) + Thunk(i64), } #[derive(Clone, Copy, Debug)] pub enum ParameterNode { Eager, - Lazy + Lazy, } pub trait TryFromArgument: Sized { @@ -25,7 +25,10 @@ pub trait TryFromArgument: Sized { } fn type_err(msg: &str) -> Signal { - Signal::Failure(RuntimeError::simple("InvalidArgumentRuntimeError", msg.to_string())) + Signal::Failure(RuntimeError::simple( + "InvalidArgumentRuntimeError", + msg.to_string(), + )) } impl TryFromArgument for Value { @@ -40,7 +43,9 @@ impl TryFromArgument for Value { impl TryFromArgument for f64 { fn try_from_argument(a: &Argument) -> Result { match a { - Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) => Ok(*n), + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) => Ok(*n), _ => Err(type_err("Expected number")), } } @@ -49,7 +54,9 @@ impl TryFromArgument for f64 { impl TryFromArgument for bool { fn try_from_argument(a: &Argument) -> Result { match a { - Argument::Eval(Value { kind: Some(Kind::BoolValue(b)) }) => Ok(*b), + Argument::Eval(Value { + kind: Some(Kind::BoolValue(b)), + }) => Ok(*b), _ => Err(type_err("Expected boolean")), } } @@ -58,7 +65,9 @@ impl TryFromArgument for bool { impl TryFromArgument for String { fn try_from_argument(a: &Argument) -> Result { match a { - Argument::Eval(Value { kind: Some(Kind::StringValue(s)) }) => Ok(s.clone()), + Argument::Eval(Value { + kind: Some(Kind::StringValue(s)), + }) => Ok(s.clone()), _ => Err(type_err("Expected string")), } } @@ -67,7 +76,9 @@ impl TryFromArgument for String { impl TryFromArgument for Struct { fn try_from_argument(a: &Argument) -> Result { match a { - Argument::Eval(Value { kind: Some(Kind::StructValue(s)) }) => Ok(s.clone()), + Argument::Eval(Value { + kind: Some(Kind::StructValue(s)), + }) => Ok(s.clone()), _ => Err(type_err("Expected struct")), } } @@ -76,7 +87,9 @@ impl TryFromArgument for Struct { impl TryFromArgument for ListValue { fn try_from_argument(a: &Argument) -> Result { match a { - Argument::Eval(Value { kind: Some(Kind::ListValue(list)) }) => Ok(list.clone()), + Argument::Eval(Value { + kind: Some(Kind::ListValue(list)), + }) => Ok(list.clone()), _ => Err(Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", "Expected array (ListValue)", @@ -89,4 +102,4 @@ impl From for RuntimeError { fn from(never: Infallible) -> Self { match never {} } -} \ No newline at end of file +} diff --git a/taurus/src/context/executor.rs b/taurus/src/context/executor.rs index e77af57..307778b 100644 --- a/taurus/src/context/executor.rs +++ b/taurus/src/context/executor.rs @@ -5,7 +5,7 @@ use crate::context::signal::Signal; use crate::error::RuntimeError; use std::cell::RefCell; use std::collections::HashMap; -use tucana::shared::{NodeFunction}; +use tucana::shared::NodeFunction; pub struct Executor<'a> { functions: &'a FunctionStore, diff --git a/taurus/src/context/macros.rs b/taurus/src/context/macros.rs index 2b17b99..27f7826 100644 --- a/taurus/src/context/macros.rs +++ b/taurus/src/context/macros.rs @@ -31,15 +31,13 @@ macro_rules! args { macro_rules! no_args { ($args_ident:ident) => { if !$args_ident.is_empty() { - return $crate::context::signal::Signal::Failure( - $crate::error::RuntimeError::simple( - "InvalidArgumentRuntimeError", - format!("Expected 0 args but received {}", $args_ident.len()), - ) - ); + return $crate::context::signal::Signal::Failure($crate::error::RuntimeError::simple( + "InvalidArgumentRuntimeError", + format!("Expected 0 args but received {}", $args_ident.len()), + )); } }; } pub(crate) use args; -pub(crate) use no_args; \ No newline at end of file +pub(crate) use no_args; diff --git a/taurus/src/context/mod.rs b/taurus/src/context/mod.rs index a8a8a25..dda7826 100644 --- a/taurus/src/context/mod.rs +++ b/taurus/src/context/mod.rs @@ -1,9 +1,9 @@ -pub mod context; -pub mod signal; pub mod argument; +pub mod context; pub mod executor; -pub mod registry; pub mod macros; +pub mod registry; +pub mod signal; use crate::error::RuntimeError; use std::{ diff --git a/taurus/src/implementation/array.rs b/taurus/src/implementation/array.rs index c051d0e..73e270b 100644 --- a/taurus/src/implementation/array.rs +++ b/taurus/src/implementation/array.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use tucana::shared::{value::Kind, ListValue, Value}; +use tucana::shared::{ListValue, Value, value::Kind}; use crate::context::argument::Argument; use crate::context::macros::args; @@ -28,7 +28,10 @@ pub fn collect_array_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { ("std::array::index_of", HandlerFn::eager(index_of, 2)), ("std::array::to_unique", HandlerFn::eager(to_unique, 1)), ("std::array::sort", HandlerFn::eager(sort, 2)), - ("std::array::sort_reverse", HandlerFn::eager(sort_reverse, 2)), + ( + "std::array::sort_reverse", + HandlerFn::eager(sort_reverse, 2), + ), ("std::array::reverse", HandlerFn::eager(reverse, 1)), ("std::array::flat", HandlerFn::eager(flat, 1)), ("std::array::min", HandlerFn::eager(min, 1)), @@ -61,16 +64,32 @@ fn at(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal fn concat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { args!(args => lhs_v: Value, rhs_v: Value); - let Kind::ListValue(lhs) = lhs_v.kind.clone().ok_or_else(|| ()).unwrap_or_else(|_| Kind::NullValue(0)) else { + let Kind::ListValue(lhs) = lhs_v + .kind + .clone() + .ok_or_else(|| ()) + .unwrap_or_else(|_| Kind::NullValue(0)) + else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received lhs={:?}", lhs_v), + format!( + "Expected two arrays as arguments but received lhs={:?}", + lhs_v + ), )); }; - let Kind::ListValue(rhs) = rhs_v.kind.clone().ok_or_else(|| ()).unwrap_or_else(|_| Kind::NullValue(0)) else { + let Kind::ListValue(rhs) = rhs_v + .kind + .clone() + .ok_or_else(|| ()) + .unwrap_or_else(|_| Kind::NullValue(0)) + else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", - format!("Expected two arrays as arguments but received rhs={:?}", rhs_v), + format!( + "Expected two arrays as arguments but received rhs={:?}", + rhs_v + ), )); }; @@ -90,7 +109,9 @@ fn filter(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Si "Expected first argument to be an array".to_string(), )); }; - let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans".to_string(), @@ -124,10 +145,18 @@ fn filter(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Si fn find(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { args!(args => array_v: Value, predicate_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); }; - let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans")); + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans", + )); }; let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); @@ -156,10 +185,18 @@ fn find(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Sign fn find_last(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { args!(args => array_v: Value, predicate_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); }; - let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans")); + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans", + )); }; let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); @@ -188,13 +225,25 @@ fn find_last(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> } } -fn find_index(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { +fn find_index( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { args!(args => array_v: Value, predicate_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); }; - let Kind::ListValue(resolved_predicate) = predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of booleans")); + let Kind::ListValue(resolved_predicate) = + predicate_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of booleans", + )); }; let mut preds: Vec = Vec::with_capacity(resolved_predicate.values.len()); @@ -263,7 +312,9 @@ fn for_each(_args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> fn map(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { // (array, transformed_results[]) args!(args => _array_v: Value, transform_v: Value); - let Kind::ListValue(transform_result) = transform_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { + let Kind::ListValue(transform_result) = + transform_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) + else { return Signal::Failure(RuntimeError::simple_str( "InvalidArgumentRuntimeError", "Expected transform result to be an array", @@ -395,10 +446,16 @@ fn sort(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Sign // array, resolved comparator yields -1/0/1 sequence args!(args => array_v: Value, cmp_v: Value); let Kind::ListValue(mut arr) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); }; let Kind::ListValue(cmp_vals) = cmp_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of numbers")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of numbers", + )); }; let mut comps: Vec = Vec::new(); @@ -424,13 +481,23 @@ fn sort(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Sign }) } -fn sort_reverse(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { +fn sort_reverse( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { args!(args => array_v: Value, cmp_v: Value); let Kind::ListValue(mut arr) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected first argument to be an array")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected first argument to be an array", + )); }; let Kind::ListValue(cmp_vals) = cmp_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { - return Signal::Failure(RuntimeError::simple_str("InvalidArgumentRuntimeError", "Expected second argument to be an array of numbers")); + return Signal::Failure(RuntimeError::simple_str( + "InvalidArgumentRuntimeError", + "Expected second argument to be an array of numbers", + )); }; let mut comps: Vec = Vec::new(); @@ -508,7 +575,10 @@ fn min(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signa Some(m) => Signal::Success(Value { kind: Some(Kind::NumberValue(*m)), }), - None => Signal::Failure(RuntimeError::simple_str("ArrayEmptyRuntimeError", "Array is empty")), + None => Signal::Failure(RuntimeError::simple_str( + "ArrayEmptyRuntimeError", + "Array is empty", + )), } } @@ -526,7 +596,10 @@ fn max(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signa Some(m) => Signal::Success(Value { kind: Some(Kind::NumberValue(*m)), }), - None => Signal::Failure(RuntimeError::simple_str("ArrayEmptyRuntimeError", "Array is empty")), + None => Signal::Failure(RuntimeError::simple_str( + "ArrayEmptyRuntimeError", + "Array is empty", + )), } } @@ -563,75 +636,142 @@ fn join(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Sign mod tests { use super::*; use crate::context::Context; - use tucana::shared::{value::Kind, ListValue, Value}; + use tucana::shared::{ListValue, Value, value::Kind}; // --- helpers ------------------------------------------------------------- - fn a_val(v: Value) -> Argument { Argument::Eval(v) } - fn v_num(n: f64) -> Value { Value { kind: Some(Kind::NumberValue(n)) } } - fn v_str(s: &str) -> Value { Value { kind: Some(Kind::StringValue(s.to_string())) } } - fn v_bool(b: bool) -> Value { Value { kind: Some(Kind::BoolValue(b)) } } - fn v_list(values: Vec) -> Value { Value { kind: Some(Kind::ListValue(ListValue { values })) } } + fn a_val(v: Value) -> Argument { + Argument::Eval(v) + } + fn v_num(n: f64) -> Value { + Value { + kind: Some(Kind::NumberValue(n)), + } + } + fn v_str(s: &str) -> Value { + Value { + kind: Some(Kind::StringValue(s.to_string())), + } + } + fn v_bool(b: bool) -> Value { + Value { + kind: Some(Kind::BoolValue(b)), + } + } + fn v_list(values: Vec) -> Value { + Value { + kind: Some(Kind::ListValue(ListValue { values })), + } + } fn expect_num(sig: Signal) -> f64 { match sig { - Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, x => panic!("Expected NumberValue, got {:?}", x), } } fn expect_str(sig: Signal) -> String { match sig { - Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, x => panic!("Expected StringValue, got {:?}", x), } } fn expect_list(sig: Signal) -> Vec { match sig { - Signal::Success(Value { kind: Some(Kind::ListValue(ListValue { values })) }) => values, + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values })), + }) => values, x => panic!("Expected ListValue, got {:?}", x), } } fn expect_bool(sig: Signal) -> bool { match sig { - Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, x => panic!("Expected BoolValue, got {:?}", x), } } fn dummy_run(_: i64) -> Signal { - Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) } // --- at ------------------------------------------------------------------ #[test] fn test_at_success() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_num(10.0), v_num(20.0), v_num(30.0)]); - assert_eq!(expect_num(at(&[a_val(arr.clone()), a_val(v_num(0.0))], &mut ctx, &mut run)), 10.0); - assert_eq!(expect_num(at(&[a_val(arr.clone()), a_val(v_num(1.0))], &mut ctx, &mut run)), 20.0); - assert_eq!(expect_num(at(&[a_val(arr), a_val(v_num(2.0))], &mut ctx, &mut run)), 30.0); + assert_eq!( + expect_num(at( + &[a_val(arr.clone()), a_val(v_num(0.0))], + &mut ctx, + &mut run + )), + 10.0 + ); + assert_eq!( + expect_num(at( + &[a_val(arr.clone()), a_val(v_num(1.0))], + &mut ctx, + &mut run + )), + 20.0 + ); + assert_eq!( + expect_num(at(&[a_val(arr), a_val(v_num(2.0))], &mut ctx, &mut run)), + 30.0 + ); } #[test] fn test_at_error() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_num(1.0)]); // wrong arg count - match at(&[a_val(arr.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match at(&[a_val(arr.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } // wrong type first arg - match at(&[a_val(v_str("not_array")), a_val(v_num(0.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match at( + &[a_val(v_str("not_array")), a_val(v_num(0.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } // wrong type second arg - match at(&[a_val(arr.clone()), a_val(v_str("x"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match at(&[a_val(arr.clone()), a_val(v_str("x"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } // oob / negative - match at(&[a_val(arr.clone()), a_val(v_num(9.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match at(&[a_val(arr), a_val(v_num(-1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match at(&[a_val(arr.clone()), a_val(v_num(9.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match at(&[a_val(arr), a_val(v_num(-1.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- concat -------------------------------------------------------------- #[test] fn test_concat_success() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let a = v_list(vec![v_num(1.0), v_num(2.0)]); let b = v_list(vec![v_num(3.0), v_num(4.0)]); let out = expect_list(concat(&[a_val(a), a_val(b)], &mut ctx, &mut run)); @@ -642,20 +782,39 @@ mod tests { #[test] fn test_concat_error() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_num(1.0)]); - match concat(&[a_val(arr.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match concat(&[a_val(v_str("not_array")), a_val(arr.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match concat(&[a_val(arr), a_val(v_num(42.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match concat(&[a_val(arr.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match concat( + &[a_val(v_str("not_array")), a_val(arr.clone())], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match concat(&[a_val(arr), a_val(v_num(42.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- filter / find / find_last / find_index ------------------------------ #[test] fn test_filter_success() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let array = v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]); let predicate = v_list(vec![v_bool(true), v_bool(false), v_bool(true)]); - let out = expect_list(filter(&[a_val(array), a_val(predicate)], &mut ctx, &mut run)); + let out = expect_list(filter( + &[a_val(array), a_val(predicate)], + &mut ctx, + &mut run, + )); assert_eq!(out.len(), 2); assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); assert_eq!(out[1].kind, Some(Kind::NumberValue(3.0))); @@ -663,78 +822,151 @@ mod tests { #[test] fn test_filter_error() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let array = v_list(vec![v_num(1.0)]); let predicate = v_list(vec![v_bool(true)]); - match filter(&[a_val(array.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match filter(&[a_val(v_str("not_array")), a_val(predicate.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match filter(&[a_val(array), a_val(v_num(1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match filter(&[a_val(array.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match filter( + &[a_val(v_str("not_array")), a_val(predicate.clone())], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match filter(&[a_val(array), a_val(v_num(1.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- first / last -------------------------------------------------------- #[test] fn test_first_success() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); - assert_eq!(expect_str(first(&[a_val(arr)], &mut ctx, &mut run)), "first"); + assert_eq!( + expect_str(first(&[a_val(arr)], &mut ctx, &mut run)), + "first" + ); } #[test] fn test_first_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - match first(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match first(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match first(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + let mut ctx = Context::new(); + let mut run = dummy_run; + match first(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match first(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match first(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } #[test] fn test_last_success() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_str("first"), v_str("second"), v_str("last")]); assert_eq!(expect_str(last(&[a_val(arr)], &mut ctx, &mut run)), "last"); } #[test] fn test_last_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - match last(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match last(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + let mut ctx = Context::new(); + let mut run = dummy_run; + match last(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match last(&[a_val(v_str("not_array"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- for_each / map ------------------------------------------------------ #[test] fn test_for_each_and_map() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; match for_each(&[], &mut ctx, &mut run) { - Signal::Success(Value { kind: Some(Kind::NullValue(_)) }) => {}, + Signal::Success(Value { + kind: Some(Kind::NullValue(_)), + }) => {} x => panic!("expected NullValue, got {:?}", x), } let transformed = v_list(vec![v_str("X"), v_str("Y")]); - let out = expect_list(map(&[a_val(v_list(vec![v_num(1.0), v_num(2.0)])), a_val(transformed.clone())], &mut ctx, &mut run)); - let expected = match transformed.kind { Some(Kind::ListValue(ListValue { values })) => values, _ => unreachable!() }; + let out = expect_list(map( + &[ + a_val(v_list(vec![v_num(1.0), v_num(2.0)])), + a_val(transformed.clone()), + ], + &mut ctx, + &mut run, + )); + let expected = match transformed.kind { + Some(Kind::ListValue(ListValue { values })) => values, + _ => unreachable!(), + }; assert_eq!(out, expected); } // --- push / pop / remove ------------------------------------------------- #[test] fn test_push_success() { - let mut ctx = Context::new(); let mut run = dummy_run; - let out = expect_list(push(&[a_val(v_list(vec![v_num(1.0), v_num(2.0)])), a_val(v_num(3.0))], &mut ctx, &mut run)); + let mut ctx = Context::new(); + let mut run = dummy_run; + let out = expect_list(push( + &[ + a_val(v_list(vec![v_num(1.0), v_num(2.0)])), + a_val(v_num(3.0)), + ], + &mut ctx, + &mut run, + )); assert_eq!(out.len(), 3); assert_eq!(out[2].kind, Some(Kind::NumberValue(3.0))); } #[test] fn test_push_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - match push(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match push(&[a_val(v_str("nope")), a_val(v_num(1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + let mut ctx = Context::new(); + let mut run = dummy_run; + match push(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match push( + &[a_val(v_str("nope")), a_val(v_num(1.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } #[test] fn test_pop_success() { - let mut ctx = Context::new(); let mut run = dummy_run; - let out = expect_list(pop(&[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], &mut ctx, &mut run)); + let mut ctx = Context::new(); + let mut run = dummy_run; + let out = expect_list(pop( + &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], + &mut ctx, + &mut run, + )); assert_eq!(out.len(), 2); assert_eq!(out[0].kind, Some(Kind::NumberValue(1.0))); assert_eq!(out[1].kind, Some(Kind::NumberValue(2.0))); @@ -742,52 +974,128 @@ mod tests { #[test] fn test_pop_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - match pop(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match pop(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + let mut ctx = Context::new(); + let mut run = dummy_run; + match pop(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match pop(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } #[test] fn test_remove_success_and_error() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; // success let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); - let out = expect_list(remove(&[a_val(arr), a_val(v_str("second"))], &mut ctx, &mut run)); + let out = expect_list(remove( + &[a_val(arr), a_val(v_str("second"))], + &mut ctx, + &mut run, + )); assert_eq!(out.len(), 2); assert_eq!(out[0].kind, Some(Kind::StringValue("first".into()))); assert_eq!(out[1].kind, Some(Kind::StringValue("third".into()))); // errors - match remove(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match remove(&[a_val(v_str("nope")), a_val(v_num(0.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match remove(&[a_val(v_list(vec![v_num(1.0)])), a_val(v_num(999.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match remove(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match remove( + &[a_val(v_str("nope")), a_val(v_num(0.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match remove( + &[a_val(v_list(vec![v_num(1.0)])), a_val(v_num(999.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- is_empty / size ----------------------------------------------------- #[test] fn test_is_empty_and_size() { - let mut ctx = Context::new(); let mut run = dummy_run; - assert!(expect_bool(is_empty(&[a_val(v_list(vec![]))], &mut ctx, &mut run))); - assert!(!expect_bool(is_empty(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run))); - assert_eq!(expect_num(size(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), 0.0); - assert_eq!(expect_num(size(&[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], &mut ctx, &mut run)), 3.0); + let mut ctx = Context::new(); + let mut run = dummy_run; + assert!(expect_bool(is_empty( + &[a_val(v_list(vec![]))], + &mut ctx, + &mut run + ))); + assert!(!expect_bool(is_empty( + &[a_val(v_list(vec![v_num(1.0)]))], + &mut ctx, + &mut run + ))); + assert_eq!( + expect_num(size(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), + 0.0 + ); + assert_eq!( + expect_num(size( + &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], + &mut ctx, + &mut run + )), + 3.0 + ); } #[test] fn test_is_empty_error_and_size_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - match is_empty(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match is_empty(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match size(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match size(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + let mut ctx = Context::new(); + let mut run = dummy_run; + match is_empty(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match is_empty(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match size(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match size(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- index_of / to_unique ------------------------------------------------ #[test] fn test_index_of_and_to_unique() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_num(10.0), v_num(42.0), v_num(30.0), v_num(42.0)]); - assert_eq!(expect_num(index_of(&[a_val(arr.clone()), a_val(v_num(42.0))], &mut ctx, &mut run)), 1.0); - match index_of(&[a_val(arr.clone()), a_val(v_num(999.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + assert_eq!( + expect_num(index_of( + &[a_val(arr.clone()), a_val(v_num(42.0))], + &mut ctx, + &mut run + )), + 1.0 + ); + match index_of( + &[a_val(arr.clone()), a_val(v_num(999.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } let uniq = expect_list(to_unique(&[a_val(arr)], &mut ctx, &mut run)); assert_eq!(uniq.len(), 3); @@ -798,41 +1106,73 @@ mod tests { #[test] fn test_index_of_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - match index_of(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match index_of(&[a_val(v_str("nope")), a_val(v_num(1.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + let mut ctx = Context::new(); + let mut run = dummy_run; + match index_of(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match index_of( + &[a_val(v_str("nope")), a_val(v_num(1.0))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- sort / sort_reverse ------------------------------------------------- #[test] fn test_sort_and_sort_reverse() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; // We don't rely on actual values; ordering is driven by the comparator sequence. let arr = v_list(vec![v_str("a"), v_str("b"), v_str("c"), v_str("d")]); let comps = v_list(vec![v_num(-1.0), v_num(1.0), v_num(0.0), v_num(-1.0)]); - let out = expect_list(sort(&[a_val(arr.clone()), a_val(comps.clone())], &mut ctx, &mut run)); + let out = expect_list(sort( + &[a_val(arr.clone()), a_val(comps.clone())], + &mut ctx, + &mut run, + )); assert_eq!(out.len(), 4); - let out_r = expect_list(sort_reverse(&[a_val(arr), a_val(comps)], &mut ctx, &mut run)); + let out_r = expect_list(sort_reverse( + &[a_val(arr), a_val(comps)], + &mut ctx, + &mut run, + )); assert_eq!(out_r.len(), 4); } // --- reverse / flat ------------------------------------------------------ #[test] fn test_reverse_success_and_error() { - let mut ctx = Context::new(); let mut run = dummy_run; - let out = expect_list(reverse(&[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], &mut ctx, &mut run)); + let mut ctx = Context::new(); + let mut run = dummy_run; + let out = expect_list(reverse( + &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], + &mut ctx, + &mut run, + )); assert_eq!(out[0].kind, Some(Kind::NumberValue(3.0))); assert_eq!(out[2].kind, Some(Kind::NumberValue(1.0))); - match reverse(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match reverse(&[], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match reverse(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match reverse(&[], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } #[test] fn test_flat_success() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let nested = v_list(vec![ v_num(1.0), v_list(vec![v_num(2.0), v_num(3.0)]), @@ -850,34 +1190,76 @@ mod tests { // --- min / max / sum ----------------------------------------------------- #[test] fn test_min_max_sum_success_and_error() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let nums = v_list(vec![v_num(5.0), v_num(1.0), v_num(8.0), v_num(2.0)]); - assert_eq!(expect_num(min(&[a_val(nums.clone())], &mut ctx, &mut run)), 1.0); - assert_eq!(expect_num(max(&[a_val(nums.clone())], &mut ctx, &mut run)), 8.0); + assert_eq!( + expect_num(min(&[a_val(nums.clone())], &mut ctx, &mut run)), + 1.0 + ); + assert_eq!( + expect_num(max(&[a_val(nums.clone())], &mut ctx, &mut run)), + 8.0 + ); assert_eq!(expect_num(sum(&[a_val(nums)], &mut ctx, &mut run)), 16.0); // empty - match min(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match max(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - assert_eq!(expect_num(sum(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), 0.0); + match min(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match max(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + assert_eq!( + expect_num(sum(&[a_val(v_list(vec![]))], &mut ctx, &mut run)), + 0.0 + ); // wrong type - match sum(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match min(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match max(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match sum(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match min(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match max(&[a_val(v_str("nope"))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } // --- join ---------------------------------------------------------------- #[test] fn test_join_success_and_error() { - let mut ctx = Context::new(); let mut run = dummy_run; + let mut ctx = Context::new(); + let mut run = dummy_run; let arr = v_list(vec![v_str("hello"), v_str("world"), v_str("test")]); - assert_eq!(expect_str(join(&[a_val(arr), a_val(v_str(", "))], &mut ctx, &mut run)), "hello, world, test"); + assert_eq!( + expect_str(join(&[a_val(arr), a_val(v_str(", "))], &mut ctx, &mut run)), + "hello, world, test" + ); // errors let arr2 = v_list(vec![v_str("hello")]); - match join(&[a_val(arr2.clone())], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match join(&[a_val(v_str("not_array")), a_val(v_str(","))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } - match join(&[a_val(arr2), a_val(v_num(42.0))], &mut ctx, &mut run) { Signal::Failure(_) => {}, x => panic!("{:?}", x) } + match join(&[a_val(arr2.clone())], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match join( + &[a_val(v_str("not_array")), a_val(v_str(","))], + &mut ctx, + &mut run, + ) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } + match join(&[a_val(arr2), a_val(v_num(42.0))], &mut ctx, &mut run) { + Signal::Failure(_) => {} + x => panic!("{:?}", x), + } } } diff --git a/taurus/src/implementation/boolean.rs b/taurus/src/implementation/boolean.rs index ae7fd92..ec7d390 100644 --- a/taurus/src/implementation/boolean.rs +++ b/taurus/src/implementation/boolean.rs @@ -8,11 +8,14 @@ use tucana::shared::{Value, value::Kind}; pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ ("std::boolean::as_number", HandlerFn::eager(as_number, 1)), - ("std::boolean::as_text", HandlerFn::eager(as_text, 1)), - ("std::boolean::from_number", HandlerFn::eager(from_number, 1)), - ("std::boolean::from_text", HandlerFn::eager(from_text, 1)), - ("std::boolean::is_equal", HandlerFn::eager(is_equal, 2)), - ("std::boolean::negate", HandlerFn::eager(negate, 1)), + ("std::boolean::as_text", HandlerFn::eager(as_text, 1)), + ( + "std::boolean::from_number", + HandlerFn::eager(from_number, 1), + ), + ("std::boolean::from_text", HandlerFn::eager(from_text, 1)), + ("std::boolean::is_equal", HandlerFn::eager(is_equal, 2)), + ("std::boolean::negate", HandlerFn::eager(negate, 1)), ] } @@ -77,38 +80,52 @@ mod tests { // ---- helpers: make Arguments ---- fn a_bool(b: bool) -> Argument { - Argument::Eval(Value { kind: Some(Kind::BoolValue(b)) }) + Argument::Eval(Value { + kind: Some(Kind::BoolValue(b)), + }) } fn a_num(n: f64) -> Argument { - Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) } fn a_str(s: &str) -> Argument { - Argument::Eval(Value { kind: Some(Kind::StringValue(s.to_string())) }) + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) } // ---- helpers: unwrap Signal ---- fn expect_num(sig: Signal) -> f64 { match sig { - Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, other => panic!("Expected NumberValue, got {:?}", other), } } fn expect_str(sig: Signal) -> String { match sig { - Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, other => panic!("Expected StringValue, got {:?}", other), } } fn expect_bool(sig: Signal) -> bool { match sig { - Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, other => panic!("Expected BoolValue, got {:?}", other), } } // dummy `run` closure (unused by these handlers) fn dummy_run(_: i64) -> Signal { - Signal::Success(Value { kind: Some(Kind::BoolValue(true)) }) + Signal::Success(Value { + kind: Some(Kind::BoolValue(true)), + }) } // ---- tests ---- @@ -117,10 +134,16 @@ mod tests { fn test_as_number_success() { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(as_number(&[a_bool(true)], &mut ctx, &mut run)), 1.0); + assert_eq!( + expect_num(as_number(&[a_bool(true)], &mut ctx, &mut run)), + 1.0 + ); let mut run = dummy_run; - assert_eq!(expect_num(as_number(&[a_bool(false)], &mut ctx, &mut run)), 0.0); + assert_eq!( + expect_num(as_number(&[a_bool(false)], &mut ctx, &mut run)), + 0.0 + ); } #[test] @@ -154,10 +177,16 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_str(as_text(&[a_bool(true)], &mut ctx, &mut run)), "true"); + assert_eq!( + expect_str(as_text(&[a_bool(true)], &mut ctx, &mut run)), + "true" + ); let mut run = dummy_run; - assert_eq!(expect_str(as_text(&[a_bool(false)], &mut ctx, &mut run)), "false"); + assert_eq!( + expect_str(as_text(&[a_bool(false)], &mut ctx, &mut run)), + "false" + ); } #[test] @@ -188,17 +217,29 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_bool(from_number(&[a_num(0.0)], &mut ctx, &mut run)), false); + assert_eq!( + expect_bool(from_number(&[a_num(0.0)], &mut ctx, &mut run)), + false + ); let mut run = dummy_run; - assert_eq!(expect_bool(from_number(&[a_num(3.5)], &mut ctx, &mut run)), true); + assert_eq!( + expect_bool(from_number(&[a_num(3.5)], &mut ctx, &mut run)), + true + ); let mut run = dummy_run; - assert_eq!(expect_bool(from_number(&[a_num(-2.0)], &mut ctx, &mut run)), true); + assert_eq!( + expect_bool(from_number(&[a_num(-2.0)], &mut ctx, &mut run)), + true + ); // -0.0 should behave like 0.0 let mut run = dummy_run; - assert_eq!(expect_bool(from_number(&[a_num(-0.0)], &mut ctx, &mut run)), false); + assert_eq!( + expect_bool(from_number(&[a_num(-0.0)], &mut ctx, &mut run)), + false + ); } #[test] @@ -230,10 +271,16 @@ mod tests { // success (case-insensitive) let mut run = dummy_run; - assert_eq!(expect_bool(from_text(&[a_str("true")], &mut ctx, &mut run)), true); + assert_eq!( + expect_bool(from_text(&[a_str("true")], &mut ctx, &mut run)), + true + ); let mut run = dummy_run; - assert_eq!(expect_bool(from_text(&[a_str("FALSE")], &mut ctx, &mut run)), false); + assert_eq!( + expect_bool(from_text(&[a_str("FALSE")], &mut ctx, &mut run)), + false + ); // errors let mut run = dummy_run; @@ -267,13 +314,26 @@ mod tests { // equalities let mut run = dummy_run; - assert_eq!(expect_bool(is_equal(&[a_bool(true), a_bool(true)], &mut ctx, &mut run)), true); + assert_eq!( + expect_bool(is_equal(&[a_bool(true), a_bool(true)], &mut ctx, &mut run)), + true + ); let mut run = dummy_run; - assert_eq!(expect_bool(is_equal(&[a_bool(false), a_bool(false)], &mut ctx, &mut run)), true); + assert_eq!( + expect_bool(is_equal( + &[a_bool(false), a_bool(false)], + &mut ctx, + &mut run + )), + true + ); let mut run = dummy_run; - assert_eq!(expect_bool(is_equal(&[a_bool(true), a_bool(false)], &mut ctx, &mut run)), false); + assert_eq!( + expect_bool(is_equal(&[a_bool(true), a_bool(false)], &mut ctx, &mut run)), + false + ); // arity/type errors let mut run = dummy_run; @@ -300,10 +360,16 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_bool(negate(&[a_bool(true)], &mut ctx, &mut run)), false); + assert_eq!( + expect_bool(negate(&[a_bool(true)], &mut ctx, &mut run)), + false + ); let mut run = dummy_run; - assert_eq!(expect_bool(negate(&[a_bool(false)], &mut ctx, &mut run)), true); + assert_eq!( + expect_bool(negate(&[a_bool(false)], &mut ctx, &mut run)), + true + ); // errors let mut run = dummy_run; diff --git a/taurus/src/implementation/http.rs b/taurus/src/implementation/http.rs index df13489..3505c8f 100644 --- a/taurus/src/implementation/http.rs +++ b/taurus/src/implementation/http.rs @@ -1,16 +1,19 @@ use crate::context::Context; +use crate::context::argument::Argument; +use crate::context::macros::args; +use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; use crate::error::RuntimeError; use tucana::shared::value::Kind; use tucana::shared::{Struct, Value}; -use crate::context::argument::Argument; -use crate::context::macros::args; -use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; pub fn collect_http_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ ("http::request::create", HandlerFn::eager(create_request, 1)), - ("http::response::create", HandlerFn::eager(create_response, 4)), + ( + "http::response::create", + HandlerFn::eager(create_response, 4), + ), ("http::control::respond", HandlerFn::eager(respond, 3)), ] } @@ -66,7 +69,11 @@ fn respond(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> S }) } -fn create_request(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { +fn create_request( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { args!(args => http_method: String, headers: Struct, http_url: String, payload: Value); let mut fields = std::collections::HashMap::new(); @@ -97,7 +104,11 @@ fn create_request(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i6 }) } -fn create_response(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { +fn create_response( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { args!(args => http_status_code: f64, headers: Struct, payload: Value); let mut fields = std::collections::HashMap::new(); diff --git a/taurus/src/implementation/mod.rs b/taurus/src/implementation/mod.rs index d2ad25e..e466e94 100644 --- a/taurus/src/implementation/mod.rs +++ b/taurus/src/implementation/mod.rs @@ -1,4 +1,4 @@ -use crate::context::registry::{HandlerFunctionEntry}; +use crate::context::registry::HandlerFunctionEntry; mod array; mod boolean; diff --git a/taurus/src/implementation/number.rs b/taurus/src/implementation/number.rs index d415cd1..d8c3695 100644 --- a/taurus/src/implementation/number.rs +++ b/taurus/src/implementation/number.rs @@ -371,7 +371,9 @@ mod tests { // ---- helpers: Arguments ---- fn a_num(n: f64) -> Argument { - Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) } fn a_str(s: &str) -> Argument { Argument::Eval(Value { @@ -382,36 +384,50 @@ mod tests { // ---- helpers: extractors ---- fn expect_num(sig: Signal) -> f64 { match sig { - Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, other => panic!("Expected NumberValue, got {:?}", other), } } fn expect_bool(sig: Signal) -> bool { match sig { - Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, other => panic!("Expected BoolValue, got {:?}", other), } } fn expect_str(sig: Signal) -> String { match sig { - Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, other => panic!("Expected StringValue, got {:?}", other), } } // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` fn dummy_run(_: i64) -> Signal { - Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) } #[test] fn test_add_and_multiply() { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(add(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run)), 8.0); + assert_eq!( + expect_num(add(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run)), + 8.0 + ); let mut run = dummy_run; - assert_eq!(expect_num(multiply(&[a_num(4.0), a_num(2.5)], &mut ctx, &mut run)), 10.0); + assert_eq!( + expect_num(multiply(&[a_num(4.0), a_num(2.5)], &mut ctx, &mut run)), + 10.0 + ); } #[test] @@ -419,10 +435,16 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(substract(&[a_num(10.0), a_num(4.0)], &mut ctx, &mut run)), 6.0); + assert_eq!( + expect_num(substract(&[a_num(10.0), a_num(4.0)], &mut ctx, &mut run)), + 6.0 + ); let mut run = dummy_run; - assert_eq!(expect_num(divide(&[a_num(15.0), a_num(3.0)], &mut ctx, &mut run)), 5.0); + assert_eq!( + expect_num(divide(&[a_num(15.0), a_num(3.0)], &mut ctx, &mut run)), + 5.0 + ); // divide by zero -> Failure let mut run = dummy_run; @@ -437,7 +459,10 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(modulo(&[a_num(10.0), a_num(3.0)], &mut ctx, &mut run)), 1.0); + assert_eq!( + expect_num(modulo(&[a_num(10.0), a_num(3.0)], &mut ctx, &mut run)), + 1.0 + ); // modulo by zero -> Failure let mut run = dummy_run; @@ -457,14 +482,26 @@ mod tests { let mut run = dummy_run; assert!(expect_bool(is_positive(&[a_num(5.0)], &mut ctx, &mut run))); let mut run = dummy_run; - assert!(!expect_bool(is_positive(&[a_num(-1.0)], &mut ctx, &mut run))); + assert!(!expect_bool(is_positive( + &[a_num(-1.0)], + &mut ctx, + &mut run + ))); let mut run = dummy_run; assert!(expect_bool(is_positive(&[a_num(0.0)], &mut ctx, &mut run))); let mut run = dummy_run; - assert!(expect_bool(is_greater(&[a_num(10.0), a_num(5.0)], &mut ctx, &mut run))); + assert!(expect_bool(is_greater( + &[a_num(10.0), a_num(5.0)], + &mut ctx, + &mut run + ))); let mut run = dummy_run; - assert!(expect_bool(is_less(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run))); + assert!(expect_bool(is_less( + &[a_num(3.0), a_num(7.0)], + &mut ctx, + &mut run + ))); let mut run = dummy_run; assert!(expect_bool(is_zero(&[a_num(0.0)], &mut ctx, &mut run))); @@ -480,7 +517,10 @@ mod tests { assert_eq!(expect_num(square(&[a_num(4.0)], &mut ctx, &mut run)), 16.0); let mut run = dummy_run; - assert_eq!(expect_num(exponential(&[a_num(2.0), a_num(3.0)], &mut ctx, &mut run)), 8.0); + assert_eq!( + expect_num(exponential(&[a_num(2.0), a_num(3.0)], &mut ctx, &mut run)), + 8.0 + ); } #[test] @@ -488,10 +528,14 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert!((expect_num(pi(&[], &mut ctx, &mut run)) - std::f64::consts::PI).abs() < f64::EPSILON); + assert!( + (expect_num(pi(&[], &mut ctx, &mut run)) - std::f64::consts::PI).abs() < f64::EPSILON + ); let mut run = dummy_run; - assert!((expect_num(euler(&[], &mut ctx, &mut run)) - std::f64::consts::E).abs() < f64::EPSILON); + assert!( + (expect_num(euler(&[], &mut ctx, &mut run)) - std::f64::consts::E).abs() < f64::EPSILON + ); let mut run = dummy_run; let inf = expect_num(infinity(&[], &mut ctx, &mut run)); @@ -503,13 +547,30 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(round_up(&[a_num(f64::consts::PI), a_num(2.0)], &mut ctx, &mut run)), 3.15); + assert_eq!( + expect_num(round_up( + &[a_num(f64::consts::PI), a_num(2.0)], + &mut ctx, + &mut run + )), + 3.15 + ); let mut run = dummy_run; - assert_eq!(expect_num(round_down(&[a_num(f64::consts::PI), a_num(2.0)], &mut ctx, &mut run)), 3.14); + assert_eq!( + expect_num(round_down( + &[a_num(f64::consts::PI), a_num(2.0)], + &mut ctx, + &mut run + )), + 3.14 + ); let mut run = dummy_run; - assert_eq!(expect_num(round(&[a_num(3.145), a_num(2.0)], &mut ctx, &mut run)), 3.15); + assert_eq!( + expect_num(round(&[a_num(3.145), a_num(2.0)], &mut ctx, &mut run)), + 3.15 + ); } #[test] @@ -517,11 +578,14 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(square_root(&[a_num(16.0)], &mut ctx, &mut run)), 4.0); + assert_eq!( + expect_num(square_root(&[a_num(16.0)], &mut ctx, &mut run)), + 4.0 + ); // cube root via exponent 1/3 let mut run = dummy_run; - let r = expect_num(root(&[a_num(8.0), a_num(1.0/3.0)], &mut ctx, &mut run)); + let r = expect_num(root(&[a_num(8.0), a_num(1.0 / 3.0)], &mut ctx, &mut run)); assert!((r - 2.0).abs() < 1e-6); let mut run = dummy_run; @@ -538,10 +602,16 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(from_text(&[a_str("42.5")], &mut ctx, &mut run)), 42.5); + assert_eq!( + expect_num(from_text(&[a_str("42.5")], &mut ctx, &mut run)), + 42.5 + ); let mut run = dummy_run; - assert_eq!(expect_str(as_text(&[a_num(42.5)], &mut ctx, &mut run)), "42.5".to_string()); + assert_eq!( + expect_str(as_text(&[a_num(42.5)], &mut ctx, &mut run)), + "42.5".to_string() + ); // from_text failure let mut run = dummy_run; @@ -556,10 +626,16 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(min(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), 3.0); + assert_eq!( + expect_num(min(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), + 3.0 + ); let mut run = dummy_run; - assert_eq!(expect_num(max(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), 7.0); + assert_eq!( + expect_num(max(&[a_num(3.0), a_num(7.0)], &mut ctx, &mut run)), + 7.0 + ); let mut run = dummy_run; assert_eq!(expect_num(negate(&[a_num(5.0)], &mut ctx, &mut run)), -5.0); @@ -579,7 +655,7 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - let s = expect_num(sin(&[a_num(f64::consts::PI/2.0)], &mut ctx, &mut run)); + let s = expect_num(sin(&[a_num(f64::consts::PI / 2.0)], &mut ctx, &mut run)); assert!((s - 1.0).abs() < 1e-12); let mut run = dummy_run; @@ -587,12 +663,12 @@ mod tests { assert!((c - 1.0).abs() < 1e-12); let mut run = dummy_run; - let t = expect_num(tan(&[a_num(f64::consts::PI/4.0)], &mut ctx, &mut run)); + let t = expect_num(tan(&[a_num(f64::consts::PI / 4.0)], &mut ctx, &mut run)); assert!((t - 1.0).abs() < 1e-4); let mut run = dummy_run; let asn = expect_num(arcsin(&[a_num(1.0)], &mut ctx, &mut run)); - assert!((asn - f64::consts::PI/2.0).abs() < 1e-12); + assert!((asn - f64::consts::PI / 2.0).abs() < 1e-12); let mut run = dummy_run; let acs = expect_num(arccos(&[a_num(1.0)], &mut ctx, &mut run)); @@ -600,7 +676,7 @@ mod tests { let mut run = dummy_run; let atn = expect_num(arctan(&[a_num(1.0)], &mut ctx, &mut run)); - assert!((atn - f64::consts::PI/4.0).abs() < 1e-12); + assert!((atn - f64::consts::PI / 4.0).abs() < 1e-12); let mut run = dummy_run; let sh = expect_num(sinh(&[a_num(0.0)], &mut ctx, &mut run)); @@ -616,18 +692,47 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(clamp(&[a_num(5.0), a_num(1.0), a_num(10.0)], &mut ctx, &mut run)), 5.0); - - let mut run = dummy_run; - assert_eq!(expect_num(clamp(&[a_num(-5.0), a_num(1.0), a_num(10.0)], &mut ctx, &mut run)), 1.0); - - let mut run = dummy_run; - assert_eq!(expect_num(clamp(&[a_num(15.0), a_num(1.0), a_num(10.0)], &mut ctx, &mut run)), 10.0); - - let mut run = dummy_run; - assert!(expect_bool(is_equal(&[a_num(5.0), a_num(5.0)], &mut ctx, &mut run))); - - let mut run = dummy_run; - assert!(!expect_bool(is_equal(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run))); + assert_eq!( + expect_num(clamp( + &[a_num(5.0), a_num(1.0), a_num(10.0)], + &mut ctx, + &mut run + )), + 5.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(clamp( + &[a_num(-5.0), a_num(1.0), a_num(10.0)], + &mut ctx, + &mut run + )), + 1.0 + ); + + let mut run = dummy_run; + assert_eq!( + expect_num(clamp( + &[a_num(15.0), a_num(1.0), a_num(10.0)], + &mut ctx, + &mut run + )), + 10.0 + ); + + let mut run = dummy_run; + assert!(expect_bool(is_equal( + &[a_num(5.0), a_num(5.0)], + &mut ctx, + &mut run + ))); + + let mut run = dummy_run; + assert!(!expect_bool(is_equal( + &[a_num(5.0), a_num(3.0)], + &mut ctx, + &mut run + ))); } } diff --git a/taurus/src/implementation/object.rs b/taurus/src/implementation/object.rs index c1060b8..7cd3ed2 100644 --- a/taurus/src/implementation/object.rs +++ b/taurus/src/implementation/object.rs @@ -1,21 +1,28 @@ -use tucana::shared::{Value, value::Kind, Struct}; +use tucana::shared::{Struct, Value, value::Kind}; -use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError}; use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; +use crate::context::signal::Signal; +use crate::{context::Context, error::RuntimeError}; pub fn collect_object_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ - ("std::object::contains_key", HandlerFn::eager(contains_key, 2)), + ( + "std::object::contains_key", + HandlerFn::eager(contains_key, 2), + ), ("std::object::keys", HandlerFn::eager(keys, 1)), ("std::object::size", HandlerFn::eager(size, 1)), ("std::object::set", HandlerFn::eager(set, 3)), ] } -fn contains_key(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { +fn contains_key( + args: &[Argument], + _ctx: &mut Context, + _run: &mut dyn FnMut(i64) -> Signal, +) -> Signal { args!(args => object: Struct, key: String); let contains = object.fields.contains_key(&key); @@ -66,21 +73,31 @@ mod tests { // ---- helpers: Value builders ---- fn v_string(s: &str) -> Value { - Value { kind: Some(Kind::StringValue(s.to_string())) } + Value { + kind: Some(Kind::StringValue(s.to_string())), + } } fn v_number(n: f64) -> Value { - Value { kind: Some(Kind::NumberValue(n)) } + Value { + kind: Some(Kind::NumberValue(n)), + } } fn v_bool(b: bool) -> Value { - Value { kind: Some(Kind::BoolValue(b)) } + Value { + kind: Some(Kind::BoolValue(b)), + } } fn v_struct(fields: HashMap) -> Value { - Value { kind: Some(Kind::StructValue(TcStruct { fields })) } + Value { + kind: Some(Kind::StructValue(TcStruct { fields })), + } } // ---- helpers: Struct builders (for args that expect Struct) ---- fn s_empty() -> TcStruct { - TcStruct { fields: HashMap::new() } + TcStruct { + fields: HashMap::new(), + } } fn s_from(mut kv: Vec<(&str, Value)>) -> TcStruct { let mut map = HashMap::::new(); @@ -92,22 +109,32 @@ mod tests { fn s_test() -> TcStruct { s_from(vec![ ("name", v_string("John")), - ("age", v_number(30.0)), + ("age", v_number(30.0)), ("active", v_bool(true)), ]) } // ---- helpers: Argument builders ---- #[allow(dead_code)] - fn a_value(v: Value) -> Argument { Argument::Eval(v) } - fn a_string(s: &str) -> Argument { Argument::Eval(Value { - kind: Some(Kind::StringValue(s.to_string())), - }) } - fn a_struct(s: TcStruct) -> Argument { Argument::Eval(Value { kind: Some(Kind::StructValue(s)) }) } + fn a_value(v: Value) -> Argument { + Argument::Eval(v) + } + fn a_string(s: &str) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) + } + fn a_struct(s: TcStruct) -> Argument { + Argument::Eval(Value { + kind: Some(Kind::StructValue(s)), + }) + } // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` fn dummy_run(_: i64) -> Signal { - Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) } #[test] @@ -118,22 +145,40 @@ mod tests { let mut run = dummy_run; let args = vec![a_struct(s_test()), a_string("name")]; let signal = contains_key(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; - match v.kind { Some(Kind::BoolValue(b)) => assert!(b), _ => panic!("Expected BoolValue") } + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::BoolValue(b)) => assert!(b), + _ => panic!("Expected BoolValue"), + } // non-existing key let mut run = dummy_run; let args = vec![a_struct(s_test()), a_string("nonexistent")]; let signal = contains_key(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; - match v.kind { Some(Kind::BoolValue(b)) => assert!(!b), _ => panic!("Expected BoolValue") } + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::BoolValue(b)) => assert!(!b), + _ => panic!("Expected BoolValue"), + } // empty object let mut run = dummy_run; let args = vec![a_struct(s_empty()), a_string("any_key")]; let signal = contains_key(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; - match v.kind { Some(Kind::BoolValue(b)) => assert!(!b), _ => panic!("Expected BoolValue") } + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::BoolValue(b)) => assert!(!b), + _ => panic!("Expected BoolValue"), + } } #[test] @@ -144,15 +189,27 @@ mod tests { let mut run = dummy_run; let args = vec![a_struct(s_test())]; let signal = size(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; - match v.kind { Some(Kind::NumberValue(n)) => assert_eq!(n, 3.0), _ => panic!("Expected NumberValue") } + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::NumberValue(n)) => assert_eq!(n, 3.0), + _ => panic!("Expected NumberValue"), + } // empty object let mut run = dummy_run; let args = vec![a_struct(s_empty())]; let signal = size(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; - match v.kind { Some(Kind::NumberValue(n)) => assert_eq!(n, 0.0), _ => panic!("Expected NumberValue") } + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; + match v.kind { + Some(Kind::NumberValue(n)) => assert_eq!(n, 0.0), + _ => panic!("Expected NumberValue"), + } } #[test] @@ -163,15 +220,27 @@ mod tests { let mut run = dummy_run; let args = vec![a_struct(s_test())]; let signal = keys(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; match v.kind { Some(Kind::ListValue(list)) => { - let mut got: Vec = list.values.iter().filter_map(|v| { - if let Some(Kind::StringValue(s)) = &v.kind { Some(s.clone()) } else { None } - }).collect(); + let mut got: Vec = list + .values + .iter() + .filter_map(|v| { + if let Some(Kind::StringValue(s)) = &v.kind { + Some(s.clone()) + } else { + None + } + }) + .collect(); got.sort(); - let mut expected = vec!["active".to_string(), "age".to_string(), "name".to_string()]; + let mut expected = + vec!["active".to_string(), "age".to_string(), "name".to_string()]; expected.sort(); assert_eq!(got, expected); } @@ -182,7 +251,10 @@ mod tests { let mut run = dummy_run; let args = vec![a_struct(s_empty())]; let signal = keys(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; match v.kind { Some(Kind::ListValue(list)) => assert_eq!(list.values.len(), 0), _ => panic!("Expected ListValue"), @@ -195,14 +267,24 @@ mod tests { // set new key let mut run = dummy_run; - let args = vec![a_struct(s_test()), a_string("email"), Argument::Eval(v_string("john@example.com"))]; + let args = vec![ + a_struct(s_test()), + a_string("email"), + Argument::Eval(v_string("john@example.com")), + ]; let signal = set(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; match v.kind { Some(Kind::StructValue(st)) => { assert_eq!(st.fields.len(), 4); match st.fields.get("email") { - Some(Value { kind: Some(Kind::StringValue(s)), .. }) => assert_eq!(s, "john@example.com"), + Some(Value { + kind: Some(Kind::StringValue(s)), + .. + }) => assert_eq!(s, "john@example.com"), _ => panic!("Expected email to be a string"), } } @@ -211,14 +293,24 @@ mod tests { // overwrite existing key let mut run = dummy_run; - let args = vec![a_struct(s_test()), a_string("age"), Argument::Eval(v_number(31.0))]; + let args = vec![ + a_struct(s_test()), + a_string("age"), + Argument::Eval(v_number(31.0)), + ]; let signal = set(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; match v.kind { Some(Kind::StructValue(st)) => { assert_eq!(st.fields.len(), 3); match st.fields.get("age") { - Some(Value { kind: Some(Kind::NumberValue(n)), .. }) => assert_eq!(*n, 31.0), + Some(Value { + kind: Some(Kind::NumberValue(n)), + .. + }) => assert_eq!(*n, 31.0), _ => panic!("Expected age to be a number"), } } @@ -232,14 +324,24 @@ mod tests { // empty object -> add first key let mut run = dummy_run; - let args = vec![a_struct(s_empty()), a_string("first_key"), Argument::Eval(v_bool(true))]; + let args = vec![ + a_struct(s_empty()), + a_string("first_key"), + Argument::Eval(v_bool(true)), + ]; let signal = set(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; match v.kind { Some(Kind::StructValue(st)) => { assert_eq!(st.fields.len(), 1); match st.fields.get("first_key") { - Some(Value { kind: Some(Kind::BoolValue(b)), .. }) => assert_eq!(*b, true), + Some(Value { + kind: Some(Kind::BoolValue(b)), + .. + }) => assert_eq!(*b, true), _ => panic!("Expected first_key to be a bool"), } } @@ -253,13 +355,23 @@ mod tests { v_struct(nf) }; let mut run = dummy_run; - let args = vec![a_struct(s_test()), a_string("address"), Argument::Eval(nested)]; + let args = vec![ + a_struct(s_test()), + a_string("address"), + Argument::Eval(nested), + ]; let signal = set(&args, &mut ctx, &mut run); - let v = match signal { Signal::Success(v) => v, _ => panic!("Expected Success") }; + let v = match signal { + Signal::Success(v) => v, + _ => panic!("Expected Success"), + }; match v.kind { Some(Kind::StructValue(st)) => { match st.fields.get("address") { - Some(Value { kind: Some(Kind::StructValue(_)), .. }) => { /* ok */ } + Some(Value { + kind: Some(Kind::StructValue(_)), + .. + }) => { /* ok */ } _ => panic!("Expected address to be a struct"), } } @@ -277,7 +389,11 @@ mod tests { let orig_clone = original.clone(); let mut run = dummy_run; - let args = vec![a_struct(original), a_string("new_key"), Argument::Eval(v_string("new_val"))]; + let args = vec![ + a_struct(original), + a_string("new_key"), + Argument::Eval(v_string("new_val")), + ]; let _ = set(&args, &mut ctx, &mut run); // ensure original (captured clone) unchanged diff --git a/taurus/src/implementation/text.rs b/taurus/src/implementation/text.rs index 93a9376..e18d8e8 100644 --- a/taurus/src/implementation/text.rs +++ b/taurus/src/implementation/text.rs @@ -24,10 +24,13 @@ pub fn collect_text_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { ("std::text::reverse", HandlerFn::eager(reverse, 1)), ("std::text::remove", HandlerFn::eager(remove, 3)), ("std::text::replace", HandlerFn::eager(replace, 3)), - ("std::text::replace_first", HandlerFn::eager(replace_first, 3)), + ( + "std::text::replace_first", + HandlerFn::eager(replace_first, 3), + ), ("std::text::replace_last", HandlerFn::eager(replace_last, 3)), - ("std::text::hex", HandlerFn::eager(hex,1)), - ("std::text::octal", HandlerFn::eager(octal,1 )), + ("std::text::hex", HandlerFn::eager(hex, 1)), + ("std::text::octal", HandlerFn::eager(octal, 1)), ("std::text::index_of", HandlerFn::eager(index_of, 2)), ("std::text::contains", HandlerFn::eager(contains, 2)), ("std::text::split", HandlerFn::eager(split, 2)), @@ -249,7 +252,7 @@ fn remove(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Si let new = chars .into_iter() .enumerate() - .filter(|&(i, _)| i < from_u || i >= to_u) + .filter(|&(i, _)| i < from_u || i >= to_u) .map(|e| e.1) .collect::(); @@ -496,52 +499,72 @@ mod tests { // ---------- helpers: build Arguments ---------- fn a_str(s: &str) -> Argument { - Argument:: Eval(Value { kind: Some(Kind::StringValue(s.to_string())) }) + Argument::Eval(Value { + kind: Some(Kind::StringValue(s.to_string())), + }) } fn a_num(n: f64) -> Argument { - Argument::Eval(Value { kind: Some(Kind::NumberValue(n)) }) + Argument::Eval(Value { + kind: Some(Kind::NumberValue(n)), + }) } fn a_list(vals: Vec) -> Argument { - Argument::Eval(Value { kind: Some(Kind::ListValue(ListValue { values: vals })) }) + Argument::Eval(Value { + kind: Some(Kind::ListValue(ListValue { values: vals })), + }) } // ---------- helpers: build bare Values ---------- fn v_str(s: &str) -> Value { - Value { kind: Some(Kind::StringValue(s.to_string())) } + Value { + kind: Some(Kind::StringValue(s.to_string())), + } } fn v_num(n: f64) -> Value { - Value { kind: Some(Kind::NumberValue(n)) } + Value { + kind: Some(Kind::NumberValue(n)), + } } // ---------- helpers: extract from Signal ---------- fn expect_num(sig: Signal) -> f64 { match sig { - Signal::Success(Value { kind: Some(Kind::NumberValue(n)) }) => n, + Signal::Success(Value { + kind: Some(Kind::NumberValue(n)), + }) => n, other => panic!("Expected NumberValue, got {:?}", other), } } fn expect_bool(sig: Signal) -> bool { match sig { - Signal::Success(Value { kind: Some(Kind::BoolValue(b)) }) => b, + Signal::Success(Value { + kind: Some(Kind::BoolValue(b)), + }) => b, other => panic!("Expected BoolValue, got {:?}", other), } } fn expect_str(sig: Signal) -> String { match sig { - Signal::Success(Value { kind: Some(Kind::StringValue(s)) }) => s, + Signal::Success(Value { + kind: Some(Kind::StringValue(s)), + }) => s, other => panic!("Expected StringValue, got {:?}", other), } } fn expect_list(sig: Signal) -> Vec { match sig { - Signal::Success(Value { kind: Some(Kind::ListValue(ListValue { values })) }) => values, + Signal::Success(Value { + kind: Some(Kind::ListValue(ListValue { values })), + }) => values, other => panic!("Expected ListValue, got {:?}", other), } } // dummy runner for handlers that accept `run: &mut dyn FnMut(i64) -> Signal` fn dummy_run(_: i64) -> Signal { - Signal::Success(Value { kind: Some(Kind::NullValue(0)) }) + Signal::Success(Value { + kind: Some(Kind::NullValue(0)), + }) } // ---------- tests ---------- @@ -557,13 +580,22 @@ mod tests { assert_eq!(bytes[0], v_num(104.0)); // 'h' let mut run = dummy_run; - assert_eq!(expect_num(byte_size(&[a_str("hello")], &mut ctx, &mut run)), 5.0); + assert_eq!( + expect_num(byte_size(&[a_str("hello")], &mut ctx, &mut run)), + 5.0 + ); // unicode: "café" -> 5 bytes, 4 chars let mut run = dummy_run; - assert_eq!(expect_num(byte_size(&[a_str("café")], &mut ctx, &mut run)), 5.0); + assert_eq!( + expect_num(byte_size(&[a_str("café")], &mut ctx, &mut run)), + 5.0 + ); let mut run = dummy_run; - assert_eq!(expect_num(length(&[a_str("café")], &mut ctx, &mut run)), 4.0); + assert_eq!( + expect_num(length(&[a_str("café")], &mut ctx, &mut run)), + 4.0 + ); } #[test] @@ -571,19 +603,34 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_str(capitalize(&[a_str("hello world")], &mut ctx, &mut run)), "Hello World"); + assert_eq!( + expect_str(capitalize(&[a_str("hello world")], &mut ctx, &mut run)), + "Hello World" + ); let mut run = dummy_run; - assert_eq!(expect_str(uppercase(&[a_str("Hello")], &mut ctx, &mut run)), "HELLO"); + assert_eq!( + expect_str(uppercase(&[a_str("Hello")], &mut ctx, &mut run)), + "HELLO" + ); let mut run = dummy_run; - assert_eq!(expect_str(lowercase(&[a_str("Hello")], &mut ctx, &mut run)), "hello"); + assert_eq!( + expect_str(lowercase(&[a_str("Hello")], &mut ctx, &mut run)), + "hello" + ); let mut run = dummy_run; - assert_eq!(expect_str(swapcase(&[a_str("HeLLo123")], &mut ctx, &mut run)), "hEllO123"); + assert_eq!( + expect_str(swapcase(&[a_str("HeLLo123")], &mut ctx, &mut run)), + "hEllO123" + ); let mut run = dummy_run; - assert_eq!(expect_str(trim(&[a_str(" hi ")], &mut ctx, &mut run)), "hi"); + assert_eq!( + expect_str(trim(&[a_str(" hi ")], &mut ctx, &mut run)), + "hi" + ); } #[test] @@ -595,7 +642,10 @@ mod tests { assert_eq!(chars_list, vec![v_str("a"), v_str("b"), v_str("c")]); let mut run = dummy_run; - assert_eq!(expect_str(at(&[a_str("hello"), a_num(1.0)], &mut ctx, &mut run)), "e"); + assert_eq!( + expect_str(at(&[a_str("hello"), a_num(1.0)], &mut ctx, &mut run)), + "e" + ); // out-of-bounds let mut run = dummy_run; @@ -616,20 +666,41 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_str(append(&[a_str("hello"), a_str(" world")], &mut ctx, &mut run)), "hello world"); + assert_eq!( + expect_str(append( + &[a_str("hello"), a_str(" world")], + &mut ctx, + &mut run + )), + "hello world" + ); let mut run = dummy_run; - assert_eq!(expect_str(prepend(&[a_str("world"), a_str("hello ")], &mut ctx, &mut run)), "hello world"); + assert_eq!( + expect_str(prepend( + &[a_str("world"), a_str("hello ")], + &mut ctx, + &mut run + )), + "hello world" + ); // insert uses BYTE index; for ASCII this matches char index let mut run = dummy_run; assert_eq!( - expect_str(insert(&[a_str("hello"), a_num(2.0), a_str("XXX")], &mut ctx, &mut run)), + expect_str(insert( + &[a_str("hello"), a_num(2.0), a_str("XXX")], + &mut ctx, + &mut run + )), "heXXXllo" ); let mut run = dummy_run; - assert_eq!(expect_num(length(&[a_str("hello")], &mut ctx, &mut run)), 5.0); + assert_eq!( + expect_num(length(&[a_str("hello")], &mut ctx, &mut run)), + 5.0 + ); } #[test] @@ -639,25 +710,41 @@ mod tests { // remove uses CHAR indices [from, to) let mut run = dummy_run; assert_eq!( - expect_str(remove(&[a_str("hello world"), a_num(2.0), a_num(7.0)], &mut ctx, &mut run)), + expect_str(remove( + &[a_str("hello world"), a_num(2.0), a_num(7.0)], + &mut ctx, + &mut run + )), "heorld" ); let mut run = dummy_run; assert_eq!( - expect_str(replace(&[a_str("hello world hello"), a_str("hello"), a_str("hi")], &mut ctx, &mut run)), + expect_str(replace( + &[a_str("hello world hello"), a_str("hello"), a_str("hi")], + &mut ctx, + &mut run + )), "hi world hi" ); let mut run = dummy_run; assert_eq!( - expect_str(replace_first(&[a_str("one two one"), a_str("one"), a_str("1")], &mut ctx, &mut run)), + expect_str(replace_first( + &[a_str("one two one"), a_str("one"), a_str("1")], + &mut ctx, + &mut run + )), "1 two one" ); let mut run = dummy_run; assert_eq!( - expect_str(replace_last(&[a_str("one two one"), a_str("one"), a_str("1")], &mut ctx, &mut run)), + expect_str(replace_last( + &[a_str("one two one"), a_str("one"), a_str("1")], + &mut ctx, + &mut run + )), "one two 1" ); } @@ -667,13 +754,19 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_str(hex(&[a_str("hello")], &mut ctx, &mut run)), "68656c6c6f"); + assert_eq!( + expect_str(hex(&[a_str("hello")], &mut ctx, &mut run)), + "68656c6c6f" + ); let mut run = dummy_run; assert_eq!(expect_str(octal(&[a_str("A")], &mut ctx, &mut run)), "101"); let mut run = dummy_run; - assert_eq!(expect_str(reverse(&[a_str("hello")], &mut ctx, &mut run)), "olleh"); + assert_eq!( + expect_str(reverse(&[a_str("hello")], &mut ctx, &mut run)), + "olleh" + ); } #[test] @@ -681,23 +774,49 @@ mod tests { let mut ctx = Context::new(); let mut run = dummy_run; - assert_eq!(expect_num(index_of(&[a_str("hello world"), a_str("world")], &mut ctx, &mut run)), 6.0); + assert_eq!( + expect_num(index_of( + &[a_str("hello world"), a_str("world")], + &mut ctx, + &mut run + )), + 6.0 + ); let mut run = dummy_run; - assert_eq!(expect_num(index_of(&[a_str("hello"), a_str("xyz")], &mut ctx, &mut run)), -1.0); + assert_eq!( + expect_num(index_of( + &[a_str("hello"), a_str("xyz")], + &mut ctx, + &mut run + )), + -1.0 + ); let mut run = dummy_run; - assert!(expect_bool(contains(&[a_str("hello world"), a_str("world")], &mut ctx, &mut run))); + assert!(expect_bool(contains( + &[a_str("hello world"), a_str("world")], + &mut ctx, + &mut run + ))); let mut run = dummy_run; let split_list = expect_list(split(&[a_str("a,b,c"), a_str(",")], &mut ctx, &mut run)); assert_eq!(split_list, vec![v_str("a"), v_str("b"), v_str("c")]); let mut run = dummy_run; - assert!(expect_bool(starts_with(&[a_str("hello"), a_str("he")], &mut ctx, &mut run))); + assert!(expect_bool(starts_with( + &[a_str("hello"), a_str("he")], + &mut ctx, + &mut run + ))); let mut run = dummy_run; - assert!(expect_bool(ends_with(&[a_str("hello"), a_str("lo")], &mut ctx, &mut run))); + assert!(expect_bool(ends_with( + &[a_str("hello"), a_str("lo")], + &mut ctx, + &mut run + ))); } #[test] @@ -710,7 +829,10 @@ mod tests { let mut run = dummy_run; let list_arg = a_list(vec![v_num(65.0), v_num(66.0), v_num(67.0)]); - assert_eq!(expect_str(from_ascii(&[list_arg], &mut ctx, &mut run)), "ABC"); + assert_eq!( + expect_str(from_ascii(&[list_arg], &mut ctx, &mut run)), + "ABC" + ); // invalid element let mut run = dummy_run; @@ -727,13 +849,21 @@ mod tests { let mut run = dummy_run; assert_eq!( - expect_str(encode(&[a_str("hello"), a_str("BASE64")], &mut ctx, &mut run)), + expect_str(encode( + &[a_str("hello"), a_str("BASE64")], + &mut ctx, + &mut run + )), "aGVsbG8=" ); let mut run = dummy_run; assert_eq!( - expect_str(decode(&[a_str("aGVsbG8="), a_str("base64")], &mut ctx, &mut run)), + expect_str(decode( + &[a_str("aGVsbG8="), a_str("base64")], + &mut ctx, + &mut run + )), "hello" ); @@ -745,8 +875,16 @@ mod tests { } let mut run = dummy_run; - assert!(expect_bool(is_equal(&[a_str("x"), a_str("x")], &mut ctx, &mut run))); + assert!(expect_bool(is_equal( + &[a_str("x"), a_str("x")], + &mut ctx, + &mut run + ))); let mut run = dummy_run; - assert!(!expect_bool(is_equal(&[a_str("x"), a_str("y")], &mut ctx, &mut run))); + assert!(!expect_bool(is_equal( + &[a_str("x"), a_str("y")], + &mut ctx, + &mut run + ))); } } diff --git a/taurus/src/main.rs b/taurus/src/main.rs index ff67944..06e2ecc 100644 --- a/taurus/src/main.rs +++ b/taurus/src/main.rs @@ -4,6 +4,8 @@ pub mod error; pub mod implementation; use crate::config::Config; +use crate::context::executor::Executor; +use crate::context::registry::FunctionStore; use crate::context::signal::Signal; use crate::implementation::collect; use code0_flow::flow_config::load_env_file; @@ -15,8 +17,6 @@ use std::collections::HashMap; use tonic_health::pb::health_server::HealthServer; use tucana::shared::value::Kind; use tucana::shared::{ExecutionFlow, NodeFunction, Value}; -use crate::context::executor::Executor; -use crate::context::registry::FunctionStore; fn handle_message(flow: ExecutionFlow, store: &FunctionStore) -> Signal { let context = Context::new(); From d6a5353d78e015c4eeca0d165af5e40f2cc01356 Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Sun, 9 Nov 2025 20:02:10 +0100 Subject: [PATCH 08/10] ref: cargo clippy --- taurus/src/context/executor.rs | 18 +++++++++--------- taurus/src/implementation/array.rs | 28 +++++++++++----------------- taurus/src/implementation/object.rs | 2 +- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/taurus/src/context/executor.rs b/taurus/src/context/executor.rs index 307778b..9d1acfc 100644 --- a/taurus/src/context/executor.rs +++ b/taurus/src/context/executor.rs @@ -90,15 +90,15 @@ impl<'a> Executor<'a> { .get(i) .copied() .unwrap_or(ParameterNode::Eager); - if matches!(mode, ParameterNode::Eager) { - if let Argument::Thunk(id) = *a { - match self.execute(id) { - Signal::Success(v) => *a = Argument::Eval(v), - s @ (Signal::Failure(_) - | Signal::Return(_) - | Signal::Respond(_) - | Signal::Stop) => return s, - } + if matches!(mode, ParameterNode::Eager) + && let Argument::Thunk(id) = *a + { + match self.execute(id) { + Signal::Success(v) => *a = Argument::Eval(v), + s @ (Signal::Failure(_) + | Signal::Return(_) + | Signal::Respond(_) + | Signal::Stop) => return s, } } } diff --git a/taurus/src/implementation/array.rs b/taurus/src/implementation/array.rs index 73e270b..af4e875 100644 --- a/taurus/src/implementation/array.rs +++ b/taurus/src/implementation/array.rs @@ -64,12 +64,7 @@ fn at(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal fn concat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signal) -> Signal { args!(args => lhs_v: Value, rhs_v: Value); - let Kind::ListValue(lhs) = lhs_v - .kind - .clone() - .ok_or_else(|| ()) - .unwrap_or_else(|_| Kind::NullValue(0)) - else { + let Kind::ListValue(lhs) = lhs_v.kind.clone().ok_or(()).unwrap_or(Kind::NullValue(0)) else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( @@ -78,12 +73,7 @@ fn concat(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Si ), )); }; - let Kind::ListValue(rhs) = rhs_v - .kind - .clone() - .ok_or_else(|| ()) - .unwrap_or_else(|_| Kind::NullValue(0)) - else { + let Kind::ListValue(rhs) = rhs_v.kind.clone().ok_or(()).unwrap_or(Kind::NullValue(0)) else { return Signal::Failure(RuntimeError::simple( "InvalidArgumentRuntimeError", format!( @@ -167,11 +157,15 @@ fn find(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Sign } let mut i = 0usize; - let item = array.values.iter().cloned().find(|_| { - let keep = *preds.get(i).unwrap_or(&false); - i += 1; - keep - }); + let item = array + .values + .iter() + .find(|&_| { + let keep = *preds.get(i).unwrap_or(&false); + i += 1; + keep + }) + .cloned(); match item { Some(v) => Signal::Success(v), diff --git a/taurus/src/implementation/object.rs b/taurus/src/implementation/object.rs index 7cd3ed2..6f0cf79 100644 --- a/taurus/src/implementation/object.rs +++ b/taurus/src/implementation/object.rs @@ -1,10 +1,10 @@ use tucana::shared::{Struct, Value, value::Kind}; +use crate::context::Context; use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError}; pub fn collect_object_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ From ebd63213b06af38da58385b00f2367dce42517b4 Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Mon, 17 Nov 2025 11:47:13 +0100 Subject: [PATCH 09/10] ref: import changes --- Cargo.lock | 26 ++++- Cargo.toml | 2 +- taurus/src/context/argument.rs | 2 +- taurus/src/context/context.rs | 33 +++++- taurus/src/context/executor.rs | 21 +++- taurus/src/context/mod.rs | 152 +-------------------------- taurus/src/context/registry.rs | 2 +- taurus/src/implementation/array.rs | 54 +++++----- taurus/src/implementation/boolean.rs | 22 ++-- taurus/src/implementation/control.rs | 2 +- taurus/src/implementation/http.rs | 2 +- taurus/src/implementation/number.rs | 30 +++--- taurus/src/implementation/object.rs | 16 +-- taurus/src/implementation/text.rs | 22 ++-- taurus/src/main.rs | 4 +- 15 files changed, 153 insertions(+), 237 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 086b42a..e701770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,7 +239,7 @@ checksum = "e3e3a3aec6ffc35ac360113d8b96206c38961afef56b0c5632cb77cd2f4429b9" dependencies = [ "serde", "serde_json", - "tucana", + "tucana 0.0.39", ] [[package]] @@ -258,7 +258,7 @@ dependencies = [ "serde_json", "tonic", "tonic-health", - "tucana", + "tucana 0.0.39", ] [[package]] @@ -1674,7 +1674,7 @@ dependencies = [ "tokio", "tonic", "tonic-health", - "tucana", + "tucana 0.0.40", ] [[package]] @@ -2012,6 +2012,26 @@ dependencies = [ "tonic-prost-build", ] +[[package]] +name = "tucana" +version = "0.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565d382969ce8de8484d22516808cfef658710a6f3342a566ad1e247ccb852cf" +dependencies = [ + "pbjson", + "pbjson-build", + "pbjson-types", + "prost", + "prost-build", + "prost-types", + "serde", + "serde_json", + "tonic", + "tonic-build", + "tonic-prost", + "tonic-prost-build", +] + [[package]] name = "typenum" version = "1.19.0" diff --git a/Cargo.toml b/Cargo.toml index 459e20c..7e154fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2024" [workspace.dependencies] code0-flow = { version = "0.0.18" } -tucana = { version = "0.0.39" } +tucana = { version = "0.0.40" } tokio = { version = "1.44.1", features = ["rt-multi-thread"] } log = "0.4.27" futures-lite = "2.6.0" diff --git a/taurus/src/context/argument.rs b/taurus/src/context/argument.rs index 7cc6105..8789fe8 100644 --- a/taurus/src/context/argument.rs +++ b/taurus/src/context/argument.rs @@ -8,7 +8,7 @@ use tucana::shared::{ListValue, Struct, Value}; pub enum Argument { // Eval => Evaluated Value // - can be consumed directly by a function - Eval(tucana::shared::Value), + Eval(Value), // Thunk of NodeFunction identifier // - used for lazy execution of nodes Thunk(i64), diff --git a/taurus/src/context/context.rs b/taurus/src/context/context.rs index 10bdeb7..3b4cdef 100644 --- a/taurus/src/context/context.rs +++ b/taurus/src/context/context.rs @@ -1 +1,32 @@ -pub struct Context {} +use crate::error::RuntimeError; +use std::collections::HashMap; +use tucana::shared::Value; + +#[derive(Clone)] +pub enum ContextResult { + Error(RuntimeError), + Success(Value), + NotFound, +} + +#[derive(Default)] +pub struct Context { + results: HashMap, +} + +impl Context { + pub fn get(&mut self, id: i64) -> ContextResult { + match self.results.get(&id) { + None => ContextResult::NotFound, + Some(result) => result.clone(), + } + } + + pub fn insert_success(&mut self, id: i64, value: Value) { + self.results.insert(id, ContextResult::Success(value)); + } + + pub fn insert_error(&mut self, id: i64, runtime_error: RuntimeError) { + self.results.insert(id, ContextResult::Error(runtime_error)); + } +} diff --git a/taurus/src/context/executor.rs b/taurus/src/context/executor.rs index 9d1acfc..05ef442 100644 --- a/taurus/src/context/executor.rs +++ b/taurus/src/context/executor.rs @@ -1,5 +1,5 @@ -use crate::context::Context; use crate::context::argument::{Argument, ParameterNode}; +use crate::context::context::{Context, ContextResult}; use crate::context::registry::FunctionStore; use crate::context::signal::Signal; use crate::error::RuntimeError; @@ -75,8 +75,23 @@ impl<'a> Executor<'a> { tucana::shared::node_value::Value::LiteralValue(val) => { args.push(Argument::Eval(val.clone())) } - tucana::shared::node_value::Value::ReferenceValue(_r) => { - unimplemented!("ReferenceValue") + tucana::shared::node_value::Value::ReferenceValue(reference) => { + let mut ctx = self.context.borrow_mut(); + let value = ctx.get(reference.node_id); + match value { + ContextResult::Error(runtime_error) => { + return Signal::Failure(runtime_error); + } + ContextResult::Success(result) => { + args.push(Argument::Eval(result.clone())); + } + ContextResult::NotFound => { + return Signal::Failure(RuntimeError::simple_str( + "ReferenceValueNotFound", + "The given node has not been executed but referenced.", + )); + } + } } tucana::shared::node_value::Value::NodeFunctionId(id) => { args.push(Argument::Thunk(*id)) diff --git a/taurus/src/context/mod.rs b/taurus/src/context/mod.rs index dda7826..7276ab0 100644 --- a/taurus/src/context/mod.rs +++ b/taurus/src/context/mod.rs @@ -3,154 +3,4 @@ pub mod context; pub mod executor; pub mod macros; pub mod registry; -pub mod signal; - -use crate::error::RuntimeError; -use std::{ - collections::{HashMap, VecDeque}, - ops::Index, -}; -use tucana::shared::{ReferenceValue, Value}; - -type NodeResult = Result; - -pub enum ContextResult { - // Will return the value / error if present of an executed node - NodeExecutionResult(NodeResult), - - // Will return the parameter of the node (indexed by the context) - ParameterResult(Value), -} - -#[derive(Clone)] -pub struct ContextEntry { - result: Result, - parameter: Vec, -} - -impl ContextEntry { - pub fn new(result: NodeResult, parameter: Vec) -> Self { - ContextEntry { result, parameter } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct ContextReference { - // Level referencing the context depth (if a node will execute nodes in itself, e.g. foreach, map...) - pub primary_level: i32, - - // Level of depth in the current context level (node after node starting_node -> next_node --> next_node --> ending_node) - pub secondary_level: i32, - - // Index of parameters as input parameter of a node - pub tertiary_level: Option, -} - -pub struct Context { - current_context_level: ContextReference, - /// A stack of environments: layer 0 is the outermost. - layers: HashMap, - /// Context Snapshot of Past Context - context_history: VecDeque<(i32, i32)>, -} - -impl Default for Context { - fn default() -> Self { - Self::new() - } -} - -impl Context { - /// Create a new, empty context. - pub fn new() -> Self { - Context { - current_context_level: ContextReference { - primary_level: 0, - secondary_level: 0, - tertiary_level: None, - }, - layers: HashMap::new(), - context_history: VecDeque::new(), - } - } - - pub fn write_to_context(&mut self, reference: ContextReference, entry: ContextEntry) { - self.layers.insert(reference, entry); - } - - pub fn write_to_current_context(&mut self, entry: ContextEntry) { - self.write_to_context(self.current_context_level.clone(), entry); - } - - pub fn set_current_context( - &mut self, - primary_level: i32, - seconday_level: i32, - tertiary_level: Option, - ) { - self.current_context_level.primary_level = primary_level; - self.current_context_level.secondary_level = seconday_level; - self.current_context_level.tertiary_level = tertiary_level; - } - - /// Will indent the context and save the past context - pub fn next_context(&mut self) { - let context_snapshot = ( - self.current_context_level.primary_level, - self.current_context_level.secondary_level, - ); - - self.context_history.push_back(context_snapshot); - - self.current_context_level.primary_level += 1; - self.current_context_level.secondary_level = 0; - } - - /// Will return to the parent context and increment the seconday level - pub fn leave_context(&mut self) { - let last_snapshot = match self.context_history.pop_back() { - Some(pair) => pair, - None => return, - }; - - self.current_context_level.primary_level = last_snapshot.0; - self.current_context_level.secondary_level = last_snapshot.1 + 1; - } - - pub fn next_node(&mut self) { - self.current_context_level.secondary_level += 1; - } - - // Looks up the current Context - pub fn get_current_context(&self) -> Option { - for (context, value) in self.layers.iter() { - if context.primary_level != self.current_context_level.primary_level { - continue; - } - - if context.secondary_level != self.current_context_level.secondary_level { - continue; - } - - if let Some(index) = self.current_context_level.tertiary_level { - let params = &value.parameter; - - let real_index = index as usize; - let value = params.index(real_index); - return Some(ContextResult::ParameterResult(value.clone())); - } - - return Some(ContextResult::NodeExecutionResult(value.result.clone())); - } - None - } - - /// Looks up the context of a reference - pub fn get(&self, reference: &ReferenceValue) -> Option { - unimplemented!("Implement latest reference pattern from Tucana 0.0.39") - } - - pub fn is_end(&self) -> bool { - self.current_context_level.primary_level == 0 - } -} +pub mod signal; \ No newline at end of file diff --git a/taurus/src/context/registry.rs b/taurus/src/context/registry.rs index 8b1fc8a..acd6ca0 100644 --- a/taurus/src/context/registry.rs +++ b/taurus/src/context/registry.rs @@ -1,4 +1,4 @@ -use crate::context::Context; +use crate::context::context::Context; use crate::context::argument::{Argument, ParameterNode}; use crate::context::signal::Signal; use std::collections::HashMap; diff --git a/taurus/src/implementation/array.rs b/taurus/src/implementation/array.rs index af4e875..d871126 100644 --- a/taurus/src/implementation/array.rs +++ b/taurus/src/implementation/array.rs @@ -6,7 +6,7 @@ use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError}; +use crate::{context::context::Context, error::RuntimeError}; pub fn collect_array_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ @@ -629,7 +629,7 @@ fn join(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Sign #[cfg(test)] mod tests { use super::*; - use crate::context::Context; + use crate::context::context::Context; use tucana::shared::{ListValue, Value, value::Kind}; // --- helpers ------------------------------------------------------------- @@ -699,7 +699,7 @@ mod tests { // --- at ------------------------------------------------------------------ #[test] fn test_at_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_num(10.0), v_num(20.0), v_num(30.0)]); @@ -727,7 +727,7 @@ mod tests { #[test] fn test_at_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_num(1.0)]); @@ -764,7 +764,7 @@ mod tests { // --- concat -------------------------------------------------------------- #[test] fn test_concat_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let a = v_list(vec![v_num(1.0), v_num(2.0)]); let b = v_list(vec![v_num(3.0), v_num(4.0)]); @@ -776,7 +776,7 @@ mod tests { #[test] fn test_concat_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_num(1.0)]); match concat(&[a_val(arr.clone())], &mut ctx, &mut run) { @@ -800,7 +800,7 @@ mod tests { // --- filter / find / find_last / find_index ------------------------------ #[test] fn test_filter_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let array = v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]); let predicate = v_list(vec![v_bool(true), v_bool(false), v_bool(true)]); @@ -816,7 +816,7 @@ mod tests { #[test] fn test_filter_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let array = v_list(vec![v_num(1.0)]); let predicate = v_list(vec![v_bool(true)]); @@ -841,7 +841,7 @@ mod tests { // --- first / last -------------------------------------------------------- #[test] fn test_first_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); assert_eq!( @@ -852,7 +852,7 @@ mod tests { #[test] fn test_first_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match first(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {} @@ -870,7 +870,7 @@ mod tests { #[test] fn test_last_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default (); let mut run = dummy_run; let arr = v_list(vec![v_str("first"), v_str("second"), v_str("last")]); assert_eq!(expect_str(last(&[a_val(arr)], &mut ctx, &mut run)), "last"); @@ -878,7 +878,7 @@ mod tests { #[test] fn test_last_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match last(&[a_val(v_list(vec![]))], &mut ctx, &mut run) { Signal::Failure(_) => {} @@ -893,7 +893,7 @@ mod tests { // --- for_each / map ------------------------------------------------------ #[test] fn test_for_each_and_map() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match for_each(&[], &mut ctx, &mut run) { Signal::Success(Value { @@ -920,7 +920,7 @@ mod tests { // --- push / pop / remove ------------------------------------------------- #[test] fn test_push_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let out = expect_list(push( &[ @@ -936,7 +936,7 @@ mod tests { #[test] fn test_push_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match push(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {} @@ -954,7 +954,7 @@ mod tests { #[test] fn test_pop_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let out = expect_list(pop( &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], @@ -968,7 +968,7 @@ mod tests { #[test] fn test_pop_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match pop(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {} @@ -982,7 +982,7 @@ mod tests { #[test] fn test_remove_success_and_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; // success let arr = v_list(vec![v_str("first"), v_str("second"), v_str("third")]); @@ -1020,7 +1020,7 @@ mod tests { // --- is_empty / size ----------------------------------------------------- #[test] fn test_is_empty_and_size() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert!(expect_bool(is_empty( &[a_val(v_list(vec![]))], @@ -1048,7 +1048,7 @@ mod tests { #[test] fn test_is_empty_error_and_size_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match is_empty(&[a_val(v_str("nope"))], &mut ctx, &mut run) { Signal::Failure(_) => {} @@ -1071,7 +1071,7 @@ mod tests { // --- index_of / to_unique ------------------------------------------------ #[test] fn test_index_of_and_to_unique() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_num(10.0), v_num(42.0), v_num(30.0), v_num(42.0)]); assert_eq!( @@ -1100,7 +1100,7 @@ mod tests { #[test] fn test_index_of_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match index_of(&[a_val(v_list(vec![v_num(1.0)]))], &mut ctx, &mut run) { Signal::Failure(_) => {} @@ -1119,7 +1119,7 @@ mod tests { // --- sort / sort_reverse ------------------------------------------------- #[test] fn test_sort_and_sort_reverse() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; // We don't rely on actual values; ordering is driven by the comparator sequence. @@ -1143,7 +1143,7 @@ mod tests { // --- reverse / flat ------------------------------------------------------ #[test] fn test_reverse_success_and_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let out = expect_list(reverse( &[a_val(v_list(vec![v_num(1.0), v_num(2.0), v_num(3.0)]))], @@ -1165,7 +1165,7 @@ mod tests { #[test] fn test_flat_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let nested = v_list(vec![ v_num(1.0), @@ -1184,7 +1184,7 @@ mod tests { // --- min / max / sum ----------------------------------------------------- #[test] fn test_min_max_sum_success_and_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let nums = v_list(vec![v_num(5.0), v_num(1.0), v_num(8.0), v_num(2.0)]); assert_eq!( @@ -1229,7 +1229,7 @@ mod tests { // --- join ---------------------------------------------------------------- #[test] fn test_join_success_and_error() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_str("hello"), v_str("world"), v_str("test")]); assert_eq!( diff --git a/taurus/src/implementation/boolean.rs b/taurus/src/implementation/boolean.rs index ec7d390..7161a13 100644 --- a/taurus/src/implementation/boolean.rs +++ b/taurus/src/implementation/boolean.rs @@ -2,7 +2,7 @@ use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError}; +use crate::{context::context::Context, error::RuntimeError}; use tucana::shared::{Value, value::Kind}; pub fn collect_boolean_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { @@ -75,7 +75,7 @@ fn negate(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Si #[cfg(test)] mod tests { use super::*; - use crate::context::Context; + use crate::context::context::Context; use tucana::shared::{Value, value::Kind}; // ---- helpers: make Arguments ---- @@ -132,7 +132,7 @@ mod tests { #[test] fn test_as_number_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( expect_num(as_number(&[a_bool(true)], &mut ctx, &mut run)), @@ -148,7 +148,7 @@ mod tests { #[test] fn test_as_number_errors() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // wrong arity: none let mut run = dummy_run; @@ -174,7 +174,7 @@ mod tests { #[test] fn test_as_text_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -191,7 +191,7 @@ mod tests { #[test] fn test_as_text_errors() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match as_text(&[], &mut ctx, &mut run) { @@ -214,7 +214,7 @@ mod tests { #[test] fn test_from_number_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -244,7 +244,7 @@ mod tests { #[test] fn test_from_number_errors() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; match from_number(&[], &mut ctx, &mut run) { @@ -267,7 +267,7 @@ mod tests { #[test] fn test_from_text_success_and_errors() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // success (case-insensitive) let mut run = dummy_run; @@ -310,7 +310,7 @@ mod tests { #[test] fn test_is_equal_and_errors() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // equalities let mut run = dummy_run; @@ -357,7 +357,7 @@ mod tests { #[test] fn test_negate_success_and_errors() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( diff --git a/taurus/src/implementation/control.rs b/taurus/src/implementation/control.rs index 8014dac..3a4957b 100644 --- a/taurus/src/implementation/control.rs +++ b/taurus/src/implementation/control.rs @@ -1,4 +1,4 @@ -use crate::context::Context; +use crate::context::context::Context; use crate::context::argument::Argument; use crate::context::argument::ParameterNode::{Eager, Lazy}; use crate::context::macros::args; diff --git a/taurus/src/implementation/http.rs b/taurus/src/implementation/http.rs index 3505c8f..6d17794 100644 --- a/taurus/src/implementation/http.rs +++ b/taurus/src/implementation/http.rs @@ -1,4 +1,4 @@ -use crate::context::Context; +use crate::context::context::Context; use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; diff --git a/taurus/src/implementation/number.rs b/taurus/src/implementation/number.rs index d8c3695..f7af46a 100644 --- a/taurus/src/implementation/number.rs +++ b/taurus/src/implementation/number.rs @@ -6,7 +6,7 @@ use crate::context::argument::Argument; use crate::context::macros::{args, no_args}; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError}; +use crate::{context::context::Context, error::RuntimeError}; pub fn collect_number_functions() -> Vec<(&'static str, HandlerFunctionEntry)> { vec![ @@ -365,7 +365,7 @@ fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> #[cfg(test)] mod tests { use super::*; - use crate::context::Context; + use crate::context::context::Context; use crate::context::argument::Argument; use tucana::shared::{Value, value::Kind}; @@ -416,7 +416,7 @@ mod tests { #[test] fn test_add_and_multiply() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( expect_num(add(&[a_num(5.0), a_num(3.0)], &mut ctx, &mut run)), @@ -432,7 +432,7 @@ mod tests { #[test] fn test_substract_and_divide() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -456,7 +456,7 @@ mod tests { #[test] fn test_modulo_and_abs() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -477,7 +477,7 @@ mod tests { #[test] fn test_comparisons_and_zero() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert!(expect_bool(is_positive(&[a_num(5.0)], &mut ctx, &mut run))); @@ -511,7 +511,7 @@ mod tests { #[test] fn test_powers_and_exponential() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!(expect_num(square(&[a_num(4.0)], &mut ctx, &mut run)), 16.0); @@ -525,7 +525,7 @@ mod tests { #[test] fn test_constants() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert!( @@ -544,7 +544,7 @@ mod tests { #[test] fn test_rounding() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -575,7 +575,7 @@ mod tests { #[test] fn test_roots_and_logs() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -599,7 +599,7 @@ mod tests { #[test] fn test_text_conversions() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -623,7 +623,7 @@ mod tests { #[test] fn test_min_max_and_negate() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -643,7 +643,7 @@ mod tests { #[test] fn test_random_range() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let r = expect_num(random(&[a_num(1.0), a_num(10.0)], &mut ctx, &mut run)); @@ -652,7 +652,7 @@ mod tests { #[test] fn test_trig_and_hyperbolic() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let s = expect_num(sin(&[a_num(f64::consts::PI / 2.0)], &mut ctx, &mut run)); @@ -689,7 +689,7 @@ mod tests { #[test] fn test_clamp_and_is_equal() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( diff --git a/taurus/src/implementation/object.rs b/taurus/src/implementation/object.rs index 6f0cf79..6f24a98 100644 --- a/taurus/src/implementation/object.rs +++ b/taurus/src/implementation/object.rs @@ -1,6 +1,6 @@ use tucana::shared::{Struct, Value, value::Kind}; -use crate::context::Context; +use crate::context::context::Context; use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; @@ -66,7 +66,7 @@ fn set(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signa #[cfg(test)] mod tests { use super::*; - use crate::context::Context; + use crate::context::context::Context; use crate::context::argument::Argument; use std::collections::HashMap; use tucana::shared::{Struct as TcStruct, Value, value::Kind}; @@ -139,7 +139,7 @@ mod tests { #[test] fn test_contains_key_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // existing key let mut run = dummy_run; @@ -183,7 +183,7 @@ mod tests { #[test] fn test_size_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // non-empty object let mut run = dummy_run; @@ -214,7 +214,7 @@ mod tests { #[test] fn test_keys_success() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // with fields let mut run = dummy_run; @@ -263,7 +263,7 @@ mod tests { #[test] fn test_set_success_and_overwrite() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // set new key let mut run = dummy_run; @@ -320,7 +320,7 @@ mod tests { #[test] fn test_set_with_empty_object_and_nested() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // empty object -> add first key let mut run = dummy_run; @@ -381,7 +381,7 @@ mod tests { #[test] fn test_set_preserves_original_struct() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let original = s_test(); let original_len = original.fields.len(); diff --git a/taurus/src/implementation/text.rs b/taurus/src/implementation/text.rs index e18d8e8..67d7e25 100644 --- a/taurus/src/implementation/text.rs +++ b/taurus/src/implementation/text.rs @@ -2,7 +2,7 @@ use crate::context::argument::Argument; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; -use crate::{context::Context, error::RuntimeError}; +use crate::{context::context::Context, error::RuntimeError}; use base64::Engine; use tucana::shared::{ListValue, Value, value::Kind}; @@ -494,7 +494,7 @@ fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> #[cfg(test)] mod tests { use super::*; - use crate::context::Context; + use crate::context::context::Context; use tucana::shared::{ListValue, Value, value::Kind}; // ---------- helpers: build Arguments ---------- @@ -571,7 +571,7 @@ mod tests { #[test] fn test_as_bytes_and_byte_size() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; // "hello" -> 5 bytes @@ -600,7 +600,7 @@ mod tests { #[test] fn test_case_ops_and_trim() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -635,7 +635,7 @@ mod tests { #[test] fn test_chars_and_at() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let chars_list = expect_list(chars(&[a_str("abc")], &mut ctx, &mut run)); @@ -663,7 +663,7 @@ mod tests { #[test] fn test_append_prepend_insert_length() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -705,7 +705,7 @@ mod tests { #[test] fn test_remove_replace_variants() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); // remove uses CHAR indices [from, to) let mut run = dummy_run; @@ -751,7 +751,7 @@ mod tests { #[test] fn test_hex_octal_reverse() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -771,7 +771,7 @@ mod tests { #[test] fn test_index_contains_split_starts_ends() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( @@ -821,7 +821,7 @@ mod tests { #[test] fn test_to_ascii_and_from_ascii() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; let ascii_vals = expect_list(to_ascii(&[a_str("AB")], &mut ctx, &mut run)); @@ -845,7 +845,7 @@ mod tests { #[test] fn test_encode_decode_base64_and_is_equal() { - let mut ctx = Context::new(); + let mut ctx = Context::default(); let mut run = dummy_run; assert_eq!( diff --git a/taurus/src/main.rs b/taurus/src/main.rs index 06e2ecc..56eec1b 100644 --- a/taurus/src/main.rs +++ b/taurus/src/main.rs @@ -9,7 +9,7 @@ use crate::context::registry::FunctionStore; use crate::context::signal::Signal; use crate::implementation::collect; use code0_flow::flow_config::load_env_file; -use context::Context; +use context::context::Context; use futures_lite::StreamExt; use log::error; use prost::Message; @@ -19,7 +19,7 @@ use tucana::shared::value::Kind; use tucana::shared::{ExecutionFlow, NodeFunction, Value}; fn handle_message(flow: ExecutionFlow, store: &FunctionStore) -> Signal { - let context = Context::new(); + let context = Context::default(); let node_functions: HashMap = flow .node_functions From ddf96cefadd9640fd49baec9ebcbddd852c4f938 Mon Sep 17 00:00:00 2001 From: raphael-goetz Date: Mon, 17 Nov 2025 11:47:43 +0100 Subject: [PATCH 10/10] ref: cargo fmt --- taurus/src/context/mod.rs | 2 +- taurus/src/context/registry.rs | 2 +- taurus/src/implementation/array.rs | 2 +- taurus/src/implementation/control.rs | 2 +- taurus/src/implementation/http.rs | 2 +- taurus/src/implementation/number.rs | 2 +- taurus/src/implementation/object.rs | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/taurus/src/context/mod.rs b/taurus/src/context/mod.rs index 7276ab0..9ca0f43 100644 --- a/taurus/src/context/mod.rs +++ b/taurus/src/context/mod.rs @@ -3,4 +3,4 @@ pub mod context; pub mod executor; pub mod macros; pub mod registry; -pub mod signal; \ No newline at end of file +pub mod signal; diff --git a/taurus/src/context/registry.rs b/taurus/src/context/registry.rs index acd6ca0..c2b399c 100644 --- a/taurus/src/context/registry.rs +++ b/taurus/src/context/registry.rs @@ -1,5 +1,5 @@ -use crate::context::context::Context; use crate::context::argument::{Argument, ParameterNode}; +use crate::context::context::Context; use crate::context::signal::Signal; use std::collections::HashMap; diff --git a/taurus/src/implementation/array.rs b/taurus/src/implementation/array.rs index d871126..c80c0a8 100644 --- a/taurus/src/implementation/array.rs +++ b/taurus/src/implementation/array.rs @@ -870,7 +870,7 @@ mod tests { #[test] fn test_last_success() { - let mut ctx = Context::default (); + let mut ctx = Context::default(); let mut run = dummy_run; let arr = v_list(vec![v_str("first"), v_str("second"), v_str("last")]); assert_eq!(expect_str(last(&[a_val(arr)], &mut ctx, &mut run)), "last"); diff --git a/taurus/src/implementation/control.rs b/taurus/src/implementation/control.rs index 3a4957b..d9671a8 100644 --- a/taurus/src/implementation/control.rs +++ b/taurus/src/implementation/control.rs @@ -1,6 +1,6 @@ -use crate::context::context::Context; use crate::context::argument::Argument; use crate::context::argument::ParameterNode::{Eager, Lazy}; +use crate::context::context::Context; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; diff --git a/taurus/src/implementation/http.rs b/taurus/src/implementation/http.rs index 6d17794..b7a7576 100644 --- a/taurus/src/implementation/http.rs +++ b/taurus/src/implementation/http.rs @@ -1,5 +1,5 @@ -use crate::context::context::Context; use crate::context::argument::Argument; +use crate::context::context::Context; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; diff --git a/taurus/src/implementation/number.rs b/taurus/src/implementation/number.rs index f7af46a..8e489a6 100644 --- a/taurus/src/implementation/number.rs +++ b/taurus/src/implementation/number.rs @@ -365,8 +365,8 @@ fn is_equal(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> #[cfg(test)] mod tests { use super::*; - use crate::context::context::Context; use crate::context::argument::Argument; + use crate::context::context::Context; use tucana::shared::{Value, value::Kind}; // ---- helpers: Arguments ---- diff --git a/taurus/src/implementation/object.rs b/taurus/src/implementation/object.rs index 6f24a98..2cbd292 100644 --- a/taurus/src/implementation/object.rs +++ b/taurus/src/implementation/object.rs @@ -1,7 +1,7 @@ use tucana::shared::{Struct, Value, value::Kind}; -use crate::context::context::Context; use crate::context::argument::Argument; +use crate::context::context::Context; use crate::context::macros::args; use crate::context::registry::{HandlerFn, HandlerFunctionEntry, IntoFunctionEntry}; use crate::context::signal::Signal; @@ -66,8 +66,8 @@ fn set(args: &[Argument], _ctx: &mut Context, _run: &mut dyn FnMut(i64) -> Signa #[cfg(test)] mod tests { use super::*; - use crate::context::context::Context; use crate::context::argument::Argument; + use crate::context::context::Context; use std::collections::HashMap; use tucana::shared::{Struct as TcStruct, Value, value::Kind};