diff --git a/Cargo.lock b/Cargo.lock index 1ca83dc..51ef96e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,7 +346,7 @@ dependencies = [ "tokio", "tonic", "tonic-health", - "tucana 0.0.70", + "tucana", "walkdir", ] @@ -1072,7 +1072,7 @@ dependencies = [ "taurus-provider", "tokio", "tonic", - "tucana 0.0.68", + "tucana", ] [[package]] @@ -1855,7 +1855,7 @@ dependencies = [ "tokio", "tonic", "tonic-health", - "tucana 0.0.68", + "tucana", ] [[package]] @@ -1868,7 +1868,7 @@ dependencies = [ "log", "rand 0.10.1", "serde_json", - "tucana 0.0.68", + "tucana", "ureq", "uuid", ] @@ -1889,7 +1889,7 @@ dependencies = [ "tokio", "tonic", "tonic-health", - "tucana 0.0.68", + "tucana", ] [[package]] @@ -1914,7 +1914,7 @@ dependencies = [ "serde", "serde_json", "taurus-core", - "tucana 0.0.68", + "tucana", ] [[package]] @@ -2260,26 +2260,6 @@ dependencies = [ "tonic-prost-build", ] -[[package]] -name = "tucana" -version = "0.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ececdc8eeccd39a9ba82a4ee78e9663d9871a9ca9da65013477536360322392" -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/crates/taurus-core/src/handler/argument.rs b/crates/taurus-core/src/handler/argument.rs index c661a15..2105138 100644 --- a/crates/taurus-core/src/handler/argument.rs +++ b/crates/taurus-core/src/handler/argument.rs @@ -7,12 +7,56 @@ use tucana::shared::value::Kind; use tucana::shared::{ListValue, NumberValue, Struct, Value}; use crate::value::{number_to_f64, number_to_i64_lossy}; +use std::fmt; +use tucana::shared::SubFlowSetting; + +#[derive(Clone)] +pub struct FunctionThunk { + pub identifier: String, + pub parameter_index: i64, + pub settings: Vec, +} + +impl fmt::Debug for FunctionThunk { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FunctionThunk") + .field("identifier", &self.identifier) + .field("parameter_index", &self.parameter_index) + .field("settings_len", &self.settings.len()) + .finish() + } +} + +#[derive(Clone)] +pub enum Thunk { + Node(i64), + Function(FunctionThunk), +} + +impl fmt::Debug for Thunk { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Thunk::Node(node_id) => write!(f, "{}", node_id), + Thunk::Function(function) => function.fmt(f), + } + } +} + +impl Thunk { + pub fn trace_target(&self) -> String { + match self { + Thunk::Node(node_id) => format!("node={}", node_id), + Thunk::Function(function) => format!("function={}", function.identifier), + } + } +} + #[derive(Clone, Debug)] pub enum Argument { /// Eager value that can be consumed immediately by a handler. Eval(Value), - /// Deferred node execution handle, evaluated by calling `run(node_id)`. - Thunk(i64), + /// Deferred execution handle, evaluated by calling `run(thunk)`. + Thunk(Thunk), } #[derive(Clone, Copy, Debug)] diff --git a/crates/taurus-core/src/handler/registry.rs b/crates/taurus-core/src/handler/registry.rs index 388b566..993d9ae 100644 --- a/crates/taurus-core/src/handler/registry.rs +++ b/crates/taurus-core/src/handler/registry.rs @@ -1,6 +1,6 @@ //! Runtime handler registry and callable function signatures. -use crate::handler::argument::{Argument, ParameterNode}; +use crate::handler::argument::{Argument, ParameterNode, Thunk}; use crate::runtime::execution::value_store::ValueStore; use crate::runtime::functions::ALL_FUNCTION_SETS; use crate::types::signal::Signal; @@ -8,12 +8,14 @@ use std::collections::HashMap; /// Handler function type. /// - 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( +/// - For lazy params, the executor will pass Argument::Thunk(thunk). +/// - If a handler wants to execute a lazy arg, it calls run(thunk). +pub type ThunkRunner<'runner> = dyn FnMut(&Thunk, &mut ValueStore) -> Signal + 'runner; + +pub type HandlerFn = for<'runner> fn( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut ThunkRunner<'runner>, ) -> Signal; #[derive(Clone, Copy)] diff --git a/crates/taurus-core/src/runtime/engine.rs b/crates/taurus-core/src/runtime/engine.rs index 220f07e..13cbc88 100644 --- a/crates/taurus-core/src/runtime/engine.rs +++ b/crates/taurus-core/src/runtime/engine.rs @@ -170,8 +170,9 @@ mod tests { use crate::types::exit_reason::ExitReason; use std::cell::RefCell; use tucana::shared::{ - InputType, ListValue, NodeParameter, NodeValue, ReferenceValue, Struct, SubFlow, Value, - node_value, reference_value, sub_flow::ExecutionReference, value::Kind, + InputType, ListValue, NodeParameter, NodeValue, ReferenceValue, Struct, SubFlow, + SubFlowSetting, Value, node_value, reference_value, sub_flow::ExecutionReference, + value::Kind, }; fn literal_param(database_id: i64, runtime_parameter_id: &str, value: Value) -> NodeParameter { @@ -200,6 +201,42 @@ mod tests { } } + fn function_thunk_param( + database_id: i64, + runtime_parameter_id: &str, + function_identifier: &str, + settings: Vec, + ) -> NodeParameter { + NodeParameter { + database_id, + runtime_parameter_id: runtime_parameter_id.to_string(), + value: Some(NodeValue { + value: Some(node_value::Value::SubFlow(SubFlow { + signature: String::new(), + settings, + execution_reference: Some(ExecutionReference::FunctionIdentifier( + function_identifier.to_string(), + )), + })), + }), + cast: None, + } + } + + fn subflow_setting( + identifier: &str, + default_value: Option, + optional: bool, + hidden: bool, + ) -> SubFlowSetting { + SubFlowSetting { + identifier: identifier.to_string(), + default_value, + optional: Some(optional), + hidden: Some(hidden), + } + } + fn node_result_ref_param( database_id: i64, runtime_parameter_id: &str, @@ -263,6 +300,13 @@ mod tests { } } + fn expect_success(signal: Signal) -> Value { + match signal { + Signal::Success(value) => value, + other => panic!("expected success, got {:?}", other), + } + } + fn input_type_ref_param( database_id: i64, runtime_parameter_id: &str, @@ -434,6 +478,247 @@ mod tests { } } + #[test] + fn function_subflow_map_executes_function_identifier_with_iteration_input() { + let engine = ExecutionEngine::new(); + + let map_node = node( + 1, + "std::list::map", + vec![ + literal_param(100, "list", list_value(vec![int_value(1), int_value(2)])), + function_thunk_param( + 101, + "transform", + "std::number::add", + vec![ + subflow_setting("lhs", None, false, false), + subflow_setting("rhs", Some(int_value(2)), false, true), + ], + ), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![map_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Success); + assert_eq!( + expect_success(signal), + list_value(vec![int_value(3), int_value(4)]) + ); + } + + #[test] + fn function_subflow_filter_executes_predicate_identifier() { + let engine = ExecutionEngine::new(); + + let filter_node = node( + 1, + "std::list::filter", + vec![ + literal_param( + 100, + "list", + list_value(vec![int_value(1), int_value(4), int_value(7)]), + ), + function_thunk_param( + 101, + "predicate", + "std::number::is_greater", + vec![ + subflow_setting("lhs", None, false, false), + subflow_setting("rhs", Some(int_value(3)), false, true), + ], + ), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![filter_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Success); + assert_eq!( + expect_success(signal), + list_value(vec![int_value(4), int_value(7)]) + ); + } + + #[test] + fn function_subflow_default_replaces_null_callback_input() { + let engine = ExecutionEngine::new(); + + let map_node = node( + 1, + "std::list::map", + vec![ + literal_param(100, "list", list_value(vec![null_value(), int_value(5)])), + function_thunk_param( + 101, + "transform", + "std::control::value", + vec![subflow_setting("value", Some(int_value(9)), false, false)], + ), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![map_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Success); + assert_eq!( + expect_success(signal), + list_value(vec![int_value(9), int_value(5)]) + ); + } + + #[test] + fn function_subflow_hidden_setting_always_uses_default() { + let engine = ExecutionEngine::new(); + + let map_node = node( + 1, + "std::list::map", + vec![ + literal_param(100, "list", list_value(vec![int_value(1), int_value(2)])), + function_thunk_param( + 101, + "transform", + "std::control::value", + vec![subflow_setting("value", Some(int_value(9)), false, true)], + ), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![map_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Success); + assert_eq!( + expect_success(signal), + list_value(vec![int_value(9), int_value(9)]) + ); + } + + #[test] + fn function_subflow_optional_missing_setting_uses_null() { + let engine = ExecutionEngine::new(); + + let if_node = node( + 1, + "std::control::if", + vec![ + literal_param( + 100, + "condition", + Value { + kind: Some(Kind::BoolValue(true)), + }, + ), + function_thunk_param( + 101, + "runnable", + "std::control::value", + vec![subflow_setting("value", None, true, false)], + ), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![if_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Success); + assert_eq!(expect_success(signal), null_value()); + } + + #[test] + fn function_subflow_required_missing_setting_fails() { + let engine = ExecutionEngine::new(); + + let if_node = node( + 1, + "std::control::if", + vec![ + literal_param( + 100, + "condition", + Value { + kind: Some(Kind::BoolValue(true)), + }, + ), + function_thunk_param( + 101, + "runnable", + "std::control::value", + vec![subflow_setting("value", None, false, false)], + ), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![if_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Failure); + match signal { + Signal::Failure(err) => assert_eq!(err.code, "T-CORE-000107"), + other => panic!("expected missing setting failure, got {:?}", other), + } + } + + #[test] + fn function_subflow_unknown_function_identifier_fails_when_executed() { + let engine = ExecutionEngine::new(); + + let if_node = node( + 1, + "std::control::if", + vec![ + literal_param( + 100, + "condition", + Value { + kind: Some(Kind::BoolValue(true)), + }, + ), + function_thunk_param(101, "runnable", "std::missing::function", Vec::new()), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![if_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Failure); + match signal { + Signal::Failure(err) => assert_eq!(err.code, "T-CORE-000002"), + other => panic!("expected function-not-found failure, got {:?}", other), + } + } + + #[test] + fn function_subflow_can_be_forced_as_eager_argument() { + let engine = ExecutionEngine::new(); + + let add_node = node( + 1, + "std::number::add", + vec![ + function_thunk_param( + 100, + "lhs", + "std::control::value", + vec![subflow_setting("value", Some(int_value(40)), false, true)], + ), + literal_param(101, "rhs", int_value(2)), + ], + None, + ); + + let (signal, reason) = engine.execute_graph(1, vec![add_node], None, None, None, false); + + assert_eq!(reason, ExitReason::Success); + assert_eq!(expect_success(signal), int_value(42)); + } + #[test] fn emitter_emits_start_and_finish_for_successful_execution() { let engine = ExecutionEngine::new(); diff --git a/crates/taurus-core/src/runtime/engine/compiler.rs b/crates/taurus-core/src/runtime/engine/compiler.rs index f0894ae..ef21b03 100644 --- a/crates/taurus-core/src/runtime/engine/compiler.rs +++ b/crates/taurus-core/src/runtime/engine/compiler.rs @@ -6,7 +6,8 @@ use tucana::shared::{NodeFunction, node_value, sub_flow}; use crate::{ runtime::engine::model::{ - CompiledArg, CompiledFlow, CompiledNode, CompiledParameter, NodeExecutionTarget, + CompiledArg, CompiledFlow, CompiledNode, CompiledParameter, CompiledThunk, + NodeExecutionTarget, }, types::errors::runtime_error::RuntimeError, }; @@ -31,11 +32,6 @@ pub enum CompileError { node_id: i64, parameter_index: usize, }, - SubFlowFunctionIdentifierUnsupported { - node_id: i64, - parameter_index: usize, - function_identifier: String, - }, } impl CompileError { @@ -84,18 +80,6 @@ impl CompileError { node_id, parameter_index ), ), - CompileError::SubFlowFunctionIdentifierUnsupported { - node_id, - parameter_index, - function_identifier, - } => RuntimeError::new( - "T-CORE-000106", - "FlowCompileError", - format!( - "Node {} parameter {} uses unsupported sub_flow function identifier {}", - node_id, parameter_index, function_identifier - ), - ), } } } @@ -160,14 +144,14 @@ pub fn compile_flow( node_value::Value::SubFlow(sub_flow) => { match sub_flow.execution_reference.as_ref() { Some(sub_flow::ExecutionReference::StartingNodeId(node_id)) => { - CompiledArg::DeferredNode(*node_id) + CompiledArg::Deferred(CompiledThunk::Node(*node_id)) } Some(sub_flow::ExecutionReference::FunctionIdentifier(identifier)) => { - return Err(CompileError::SubFlowFunctionIdentifierUnsupported { - node_id: node.database_id, - parameter_index, - function_identifier: identifier.clone(), - }); + CompiledArg::Deferred(CompiledThunk::Function { + identifier: identifier.clone(), + parameter_index: parameter_index as i64, + settings: sub_flow.settings.clone(), + }) } None => { return Err(CompileError::SubFlowExecutionReferenceMissing { diff --git a/crates/taurus-core/src/runtime/engine/executor.rs b/crates/taurus-core/src/runtime/engine/executor.rs index f6fec8d..fc28e44 100644 --- a/crates/taurus-core/src/runtime/engine/executor.rs +++ b/crates/taurus-core/src/runtime/engine/executor.rs @@ -8,13 +8,18 @@ use tucana::aquila::ActionExecutionRequest; use tucana::shared::node_execution_result::Result as TucanaNodeResult; use tucana::shared::reference_value::Target; use tucana::shared::value::Kind; -use tucana::shared::{NodeExecutionResult as TucanaNodeExecutionResult, Struct, Value}; +use tucana::shared::{ + InputType, NodeExecutionResult as TucanaNodeExecutionResult, ReferenceValue, Struct, + SubFlowSetting, Value, +}; use uuid::Uuid; -use crate::handler::argument::{Argument, ParameterNode}; +use crate::handler::argument::{Argument, FunctionThunk, ParameterNode, Thunk}; use crate::handler::registry::{FunctionStore, HandlerFunctionEntry}; use crate::runtime::engine::emitter::{EmitType, ExecutionId, RespondEmitter}; -use crate::runtime::engine::model::{CompiledArg, CompiledFlow, CompiledNode, NodeExecutionTarget}; +use crate::runtime::engine::model::{ + CompiledArg, CompiledFlow, CompiledNode, CompiledThunk, NodeExecutionTarget, +}; use crate::runtime::execution::trace::{ ArgKind, ArgTrace, EdgeKind, Outcome, ReferenceKind, TraceRun, }; @@ -151,6 +156,75 @@ impl<'a> EngineExecutor<'a> { } } + fn execute_thunk(&self, thunk: &Thunk, value_store: &mut ValueStore) -> ExecutionResult { + match thunk { + Thunk::Node(node_id) => self.execute_from_node_id(*node_id, value_store), + Thunk::Function(function) => self.execute_function_thunk(function, value_store), + } + } + + fn execute_function_thunk( + &self, + function: &FunctionThunk, + value_store: &mut ValueStore, + ) -> ExecutionResult { + let entry = match self.handlers.get(function.identifier.as_str()).copied() { + Some(entry) => entry, + None => { + return ExecutionResult { + signal: Signal::Failure(RuntimeError::new( + "T-CORE-000002", + "FunctionNotFound", + format!("Function {} not found", function.identifier), + )), + root_frame: None, + }; + } + }; + + let frame_id = self.trace_enter_function( + value_store.get_current_node_id(), + function.identifier.as_str(), + value_store, + ); + + let mut args = match self.build_function_thunk_args(function, value_store, frame_id) { + Ok(args) => args, + Err(err) => { + let signal = Signal::Failure(err); + self.trace_exit(frame_id, &signal, value_store); + return ExecutionResult { + signal, + root_frame: frame_id, + }; + } + }; + + let signal = + if let Some(signal) = self.force_eager_args(&entry, &mut args, value_store, frame_id) { + signal + } else { + let mut run = |thunk: &Thunk, store: &mut ValueStore| { + self.trace_mark_thunk_executed(frame_id, thunk); + let label = store.pop_runtime_trace_label(); + let child_result = self.execute_thunk(thunk, store); + if let (Some(parent), Some(child)) = (frame_id, child_result.root_frame) { + self.trace_link_child(parent, child, EdgeKind::RuntimeCall { label }); + } + child_result.signal + }; + + (entry.handler)(&args, value_store, &mut run) + }; + + self.trace_exit(frame_id, &signal, value_store); + + ExecutionResult { + signal, + root_frame: frame_id, + } + } + fn execute_single_node(&self, node_idx: usize, value_store: &mut ValueStore) -> NodeResult { let node = &self.flow.nodes[node_idx]; // InputType references resolve against the currently running node. @@ -198,10 +272,10 @@ impl<'a> EngineExecutor<'a> { } // Handler-owned runtime calls (for lazy args / callbacks) re-enter the same executor. - let mut run = |node_id: i64, store: &mut ValueStore| { - self.trace_mark_thunk_executed_by_node(frame_id, node_id); + let mut run = |thunk: &Thunk, store: &mut ValueStore| { + self.trace_mark_thunk_executed(frame_id, thunk); let label = store.pop_runtime_trace_label(); - let child_result = self.execute_from_node_id(node_id, store); + let child_result = self.execute_thunk(thunk, store); if let (Some(parent), Some(child)) = (frame_id, child_result.root_frame) { self.trace_link_child(parent, child, EdgeKind::RuntimeCall { label }); } @@ -377,20 +451,22 @@ impl<'a> EngineExecutor<'a> { )); } }, - CompiledArg::DeferredNode(node_id) => { + CompiledArg::Deferred(thunk) => { + let thunk = compiled_thunk_to_argument(thunk); + let target = thunk.trace_target(); self.trace_record_arg( frame_id, ArgTrace { index, kind: ArgKind::Thunk { - node_id: *node_id, + target: target.clone(), eager: false, executed: false, }, - preview: format!("thunk({})", node_id), + preview: format!("thunk({})", target), }, ); - args.push(Argument::Thunk(*node_id)); + args.push(Argument::Thunk(thunk)); } } } @@ -398,6 +474,40 @@ impl<'a> EngineExecutor<'a> { Ok(args) } + fn build_function_thunk_args( + &self, + function: &FunctionThunk, + value_store: &mut ValueStore, + frame_id: Option, + ) -> Result, RuntimeError> { + let mut args = Vec::with_capacity(function.settings.len()); + let current_node_id = value_store.get_current_node_id(); + + for (index, setting) in function.settings.iter().enumerate() { + let input_type = InputType { + node_id: current_node_id, + parameter_index: function.parameter_index, + input_index: index as i64, + }; + let value = resolve_function_setting(function, setting, input_type, value_store)?; + self.trace_record_arg( + frame_id, + ArgTrace { + index, + kind: ArgKind::Literal, + preview: format!( + "setting({}) -> {}", + setting.identifier, + preview_value(&value) + ), + }, + ); + args.push(Argument::Eval(value)); + } + + Ok(args) + } + fn force_eager_args( &self, entry: &HandlerFunctionEntry, @@ -409,10 +519,10 @@ impl<'a> EngineExecutor<'a> { let mode = entry.param_mode(index); if matches!(mode, ParameterNode::Eager) - && let Argument::Thunk(node_id) = *argument + && let Argument::Thunk(thunk) = argument { self.trace_mark_thunk(frame_id, index, true, true); - let child = self.execute_from_node_id(node_id, value_store); + let child = self.execute_thunk(thunk, value_store); if let (Some(parent), Some(child_root)) = (frame_id, child.root_frame) { self.trace_link_child( parent, @@ -446,10 +556,10 @@ impl<'a> EngineExecutor<'a> { for (index, argument) in args.iter_mut().enumerate() { match argument { Argument::Eval(value) => values.push(value.clone()), - Argument::Thunk(node_id) => { + Argument::Thunk(thunk) => { // Remote execution always receives materialized values, never thunks. self.trace_mark_thunk(frame_id, index, true, true); - let child = self.execute_from_node_id(*node_id, value_store); + let child = self.execute_thunk(thunk, value_store); if let (Some(parent), Some(child_root)) = (frame_id, child.root_frame) { self.trace_link_child( parent, @@ -535,12 +645,19 @@ impl<'a> EngineExecutor<'a> { } fn trace_enter(&self, node: &CompiledNode, value_store: &ValueStore) -> Option { + self.trace_enter_function(node.id, node.handler_id.as_str(), value_store) + } + + fn trace_enter_function( + &self, + node_id: i64, + function_name: &str, + value_store: &ValueStore, + ) -> Option { self.tracer.map(|tracer| { - tracer.borrow_mut().enter_node( - node.id, - node.handler_id.as_str(), - value_store.trace_snapshot(), - ) + tracer + .borrow_mut() + .enter_node(node_id, function_name, value_store.trace_snapshot()) }) } @@ -598,15 +715,89 @@ impl<'a> EngineExecutor<'a> { } } - fn trace_mark_thunk_executed_by_node(&self, frame_id: Option, node_id: i64) { + fn trace_mark_thunk_executed(&self, frame_id: Option, thunk: &Thunk) { if let (Some(frame_id), Some(tracer)) = (frame_id, self.tracer) { tracer .borrow_mut() - .mark_thunk_executed_by_node(frame_id, node_id); + .mark_thunk_executed(frame_id, thunk.trace_target().as_str()); } } } +fn compiled_thunk_to_argument(thunk: &CompiledThunk) -> Thunk { + match thunk { + CompiledThunk::Node(node_id) => Thunk::Node(*node_id), + CompiledThunk::Function { + identifier, + parameter_index, + settings, + } => Thunk::Function(FunctionThunk { + identifier: identifier.clone(), + parameter_index: *parameter_index, + settings: settings.clone(), + }), + } +} + +fn resolve_function_setting( + function: &FunctionThunk, + setting: &SubFlowSetting, + input_type: InputType, + value_store: &mut ValueStore, +) -> Result { + if setting.hidden.unwrap_or(false) { + return Ok(setting_default_or_null(setting)); + } + + let reference = ReferenceValue { + target: Some(Target::InputType(input_type)), + paths: Vec::new(), + }; + + match value_store.get(reference) { + ValueStoreResult::Success(value) => { + if is_null_value(&value) + && let Some(default_value) = setting.default_value.clone() + { + Ok(default_value) + } else { + Ok(value) + } + } + ValueStoreResult::Error(err) => Err(err), + ValueStoreResult::NotFound => { + if let Some(default_value) = setting.default_value.clone() { + Ok(default_value) + } else if setting.optional.unwrap_or(false) { + Ok(null_value()) + } else { + Err(RuntimeError::new( + "T-CORE-000107", + "SubFlowSettingValueMissing", + format!( + "Required sub_flow setting {} for function {} is missing", + setting.identifier, function.identifier + ), + )) + } + } + } +} + +fn setting_default_or_null(setting: &SubFlowSetting) -> Value { + setting.default_value.clone().unwrap_or_else(null_value) +} + +fn is_null_value(value: &Value) -> bool { + matches!(value.kind.as_ref(), None | Some(Kind::NullValue(_))) +} + +fn null_value() -> Value { + Value { + kind: Some(Kind::NullValue(0)), + } +} + fn preview_value(value: &Value) -> String { // Trace previews are deterministic and human-readable for debugging snapshots. format_value_json(value) diff --git a/crates/taurus-core/src/runtime/engine/model.rs b/crates/taurus-core/src/runtime/engine/model.rs index 50ff1e4..97eeefa 100644 --- a/crates/taurus-core/src/runtime/engine/model.rs +++ b/crates/taurus-core/src/runtime/engine/model.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; -use tucana::shared::{ReferenceValue, Value}; +use tucana::shared::{ReferenceValue, SubFlowSetting, Value}; #[derive(Debug, Clone)] pub enum NodeExecutionTarget { @@ -18,7 +18,17 @@ pub enum NodeExecutionTarget { pub enum CompiledArg { Literal(Value), Reference(ReferenceValue), - DeferredNode(i64), + Deferred(CompiledThunk), +} + +#[derive(Debug, Clone)] +pub enum CompiledThunk { + Node(i64), + Function { + identifier: String, + parameter_index: i64, + settings: Vec, + }, } /// Compiled parameter binding. diff --git a/crates/taurus-core/src/runtime/execution/render.rs b/crates/taurus-core/src/runtime/execution/render.rs index e0442c1..ef7d89a 100644 --- a/crates/taurus-core/src/runtime/execution/render.rs +++ b/crates/taurus-core/src/runtime/execution/render.rs @@ -160,13 +160,13 @@ fn render_frame( format!("reference {:?} ({})", reference, hit_state) } ArgKind::Thunk { - node_id, + target, eager, executed, } => { let mode = if *eager { "eager" } else { "lazy" }; let executed_state = if *executed { "executed" } else { "deferred" }; - format!("thunk node={} {} {}", node_id, mode, executed_state) + format!("thunk {} {} {}", target, mode, executed_state) } }; out.push_str(&format!( diff --git a/crates/taurus-core/src/runtime/execution/trace.rs b/crates/taurus-core/src/runtime/execution/trace.rs index 940656f..070726b 100644 --- a/crates/taurus-core/src/runtime/execution/trace.rs +++ b/crates/taurus-core/src/runtime/execution/trace.rs @@ -31,7 +31,7 @@ pub enum ArgKind { hit: bool, }, Thunk { - node_id: i64, + target: String, eager: bool, executed: bool, }, diff --git a/crates/taurus-core/src/runtime/execution/tracer.rs b/crates/taurus-core/src/runtime/execution/tracer.rs index 894060d..8d712b9 100644 --- a/crates/taurus-core/src/runtime/execution/tracer.rs +++ b/crates/taurus-core/src/runtime/execution/tracer.rs @@ -14,7 +14,7 @@ pub trait ExecutionTracer { fn record_arg(&mut self, frame_id: u64, arg: ArgTrace); fn link_child(&mut self, parent_frame: u64, child_frame: u64, edge: EdgeKind); fn mark_thunk(&mut self, frame_id: u64, arg_index: usize, eager: bool, executed: bool); - fn mark_thunk_executed_by_node(&mut self, frame_id: u64, node_id: i64); + fn mark_thunk_executed(&mut self, frame_id: u64, target: &str); fn exit_node(&mut self, frame_id: u64, outcome: Outcome, store_after: StoreSnapshot); } @@ -141,16 +141,16 @@ impl ExecutionTracer for Tracer { } } - fn mark_thunk_executed_by_node(&mut self, frame_id: u64, node_id: i64) { + fn mark_thunk_executed(&mut self, frame_id: u64, target: &str) { let frame = self.get_frame_mut(frame_id); if let Some(arg) = frame.args.iter_mut().find(|a| { matches!( - a.kind, + &a.kind, ArgKind::Thunk { - node_id: current_node, + target: current_target, executed: false, .. - } if current_node == node_id + } if current_target == target ) }) && let ArgTrace { kind: diff --git a/crates/taurus-core/src/runtime/functions/array.rs b/crates/taurus-core/src/runtime/functions/array.rs index 7686c5e..41780c7 100644 --- a/crates/taurus-core/src/runtime/functions/array.rs +++ b/crates/taurus-core/src/runtime/functions/array.rs @@ -76,9 +76,9 @@ fn fail(category: &str, message: impl Into) -> Signal { fn parse_array_and_thunk<'a>( op_name: &str, args: &'a [Argument], -) -> Result<(&'a Value, i64), Signal> { +) -> Result<(&'a Value, &'a crate::handler::argument::Thunk), Signal> { match args { - [Argument::Eval(array_v), Argument::Thunk(thunk)] => Ok((array_v, *thunk)), + [Argument::Eval(array_v), Argument::Thunk(thunk)] => Ok((array_v, thunk)), _ => Err(fail( "InvalidArgumentRuntimeError", format!( @@ -103,8 +103,8 @@ fn run_with_unary_input( input_type: InputType, iter_index: usize, item: &Value, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, - thunk_node: i64, + run: &mut crate::handler::registry::ThunkRunner<'_>, + thunk_node: &crate::handler::argument::Thunk, ) -> Signal { ctx.insert_input_type(input_type, item.clone()); ctx.push_runtime_trace_label(format!("iter={} value={}", iter_index, preview_value(item))); @@ -120,8 +120,8 @@ fn run_with_binary_inputs( cmp_index: usize, left: &Value, right: &Value, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, - thunk_node: i64, + run: &mut crate::handler::registry::ThunkRunner<'_>, + thunk_node: &crate::handler::argument::Thunk, ) -> Signal { ctx.insert_input_type(left_input, left.clone()); ctx.insert_input_type(right_input, right.clone()); @@ -178,7 +178,7 @@ fn comparator_ordering(signal: Signal, reverse: bool) -> Result Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { // array, index args!(args => array: ListValue, index: f64); @@ -203,7 +203,7 @@ fn at( fn concat( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs_v: Value, rhs_v: Value); @@ -237,7 +237,7 @@ fn concat( fn filter( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, predicate_node) = match parse_array_and_thunk("filter", args) { Ok(data) => data, @@ -273,7 +273,7 @@ fn filter( fn find( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, predicate_node) = match parse_array_and_thunk("find", args) { Ok(data) => data, @@ -308,7 +308,7 @@ fn find( fn find_last( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, predicate_node) = match parse_array_and_thunk("find_last", args) { Ok(data) => data, @@ -344,7 +344,7 @@ fn find_last( fn find_index( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, predicate_node) = match parse_array_and_thunk("find_index", args) { Ok(data) => data, @@ -380,7 +380,7 @@ fn find_index( fn first( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array: ListValue); @@ -397,7 +397,7 @@ fn first( fn last( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array: ListValue); match array.values.last() { @@ -413,7 +413,7 @@ fn last( fn for_each( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, transform_node) = match parse_array_and_thunk("for_each", args) { Ok(data) => data, @@ -474,7 +474,7 @@ fn format_value_json(value: &Value) -> String { fn map( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, transform_node) = match parse_array_and_thunk("map", args) { Ok(data) => data, @@ -505,7 +505,7 @@ fn map( fn push( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value, item: Value); let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -524,7 +524,7 @@ fn push( fn pop( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value); let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -543,7 +543,7 @@ fn pop( fn remove( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value, item: Value); let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -570,7 +570,7 @@ fn remove( fn is_empty( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -588,7 +588,7 @@ fn is_empty( fn size( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -604,7 +604,7 @@ fn size( fn index_of( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value, item: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -627,7 +627,7 @@ fn index_of( fn to_unique( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -653,7 +653,7 @@ fn to_unique( fn sort( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, transform_node) = match parse_array_and_thunk("sort", args) { Ok(data) => data, @@ -718,7 +718,7 @@ fn sort( fn sort_reverse( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let (array_v, transform_node) = match parse_array_and_thunk("sort_reverse", args) { Ok(data) => data, @@ -783,7 +783,7 @@ fn sort_reverse( fn reverse( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value); let Kind::ListValue(mut array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -802,7 +802,7 @@ fn reverse( fn flat( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array_v: Value); let Kind::ListValue(array) = array_v.kind.ok_or(()).unwrap_or(Kind::NullValue(0)) else { @@ -829,7 +829,7 @@ fn flat( fn min( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array: ListValue); @@ -869,7 +869,7 @@ fn min( fn max( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array: ListValue); @@ -909,7 +909,7 @@ fn max( fn sum( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array: ListValue); @@ -949,7 +949,7 @@ fn sum( fn join( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => array: ListValue, separator: String); @@ -976,7 +976,7 @@ mod tests { Argument::Eval(v) } fn a_thunk(id: i64) -> Argument { - Argument::Thunk(id) + Argument::Thunk(crate::handler::argument::Thunk::Node(id)) } fn v_num(n: f64) -> Value { value_from_f64(n) @@ -1033,13 +1033,15 @@ mod tests { } } - fn dummy_run(_: i64, _: &mut ValueStore) -> Signal { + fn dummy_run(_: &crate::handler::argument::Thunk, _: &mut ValueStore) -> Signal { Signal::Success(Value { kind: Some(Kind::NullValue(0)), }) } - fn run_from_bools(seq: Vec) -> impl FnMut(i64, &mut ValueStore) -> Signal { + fn run_from_bools( + seq: Vec, + ) -> impl FnMut(&crate::handler::argument::Thunk, &mut ValueStore) -> Signal { let mut i = 0usize; move |_, _| { let b = *seq.get(i).unwrap_or(&false); @@ -1050,7 +1052,9 @@ mod tests { } } - fn run_from_values(seq: Vec) -> impl FnMut(i64, &mut ValueStore) -> Signal { + fn run_from_values( + seq: Vec, + ) -> impl FnMut(&crate::handler::argument::Thunk, &mut ValueStore) -> Signal { let mut i = 0usize; move |_, _| { let v = seq.get(i).cloned().unwrap_or(Value { @@ -1251,7 +1255,7 @@ mod tests { fn test_for_each_and_map() { let mut ctx = ValueStore::default(); let mut called = 0usize; - let mut run = |_, _ctx: &mut ValueStore| { + let mut run = |_: &crate::handler::argument::Thunk, _ctx: &mut ValueStore| { called += 1; Signal::Success(Value { kind: Some(Kind::NullValue(0)), @@ -1291,7 +1295,7 @@ mod tests { let return_value = v_str("early_return"); let mut for_each_calls = 0usize; - let mut for_each_run = |_, _ctx: &mut ValueStore| { + let mut for_each_run = |_: &crate::handler::argument::Thunk, _ctx: &mut ValueStore| { for_each_calls += 1; Signal::Return(return_value.clone()) }; @@ -1309,7 +1313,7 @@ mod tests { assert_eq!(for_each_calls, 2); let mut map_calls = 0usize; - let mut map_run = |_, _ctx: &mut ValueStore| { + let mut map_run = |_: &crate::handler::argument::Thunk, _ctx: &mut ValueStore| { map_calls += 1; Signal::Return(return_value.clone()) }; @@ -1338,7 +1342,7 @@ mod tests { let mut filter_index = 0usize; let filter_returns = [true, false, true]; - let mut filter_run = |_, _ctx: &mut ValueStore| { + let mut filter_run = |_: &crate::handler::argument::Thunk, _ctx: &mut ValueStore| { let out = filter_returns.get(filter_index).copied().unwrap_or(false); filter_index += 1; Signal::Return(v_bool(out)) @@ -1355,7 +1359,7 @@ mod tests { let mut find_index = 0usize; let find_returns = [false, true]; - let mut find_run = |_, _ctx: &mut ValueStore| { + let mut find_run = |_: &crate::handler::argument::Thunk, _ctx: &mut ValueStore| { let out = find_returns.get(find_index).copied().unwrap_or(false); find_index += 1; Signal::Return(v_bool(out)) diff --git a/crates/taurus-core/src/runtime/functions/boolean.rs b/crates/taurus-core/src/runtime/functions/boolean.rs index c097f54..7dffd93 100644 --- a/crates/taurus-core/src/runtime/functions/boolean.rs +++ b/crates/taurus-core/src/runtime/functions/boolean.rs @@ -24,7 +24,7 @@ pub(crate) const FUNCTIONS: &[FunctionRegistration] = &[ fn as_number( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: bool); Signal::Success(value_from_i64(if value { 1 } else { 0 })) @@ -33,7 +33,7 @@ fn as_number( fn as_text( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: bool); Signal::Success(Value { @@ -44,7 +44,7 @@ fn as_text( fn from_number( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => number: f64); let is_zero = number == 0.0; @@ -56,7 +56,7 @@ fn from_number( fn from_text( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => text: String); @@ -76,7 +76,7 @@ fn from_text( fn is_equal( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: bool, rhs: bool); Signal::Success(Value { @@ -87,7 +87,7 @@ fn is_equal( fn negate( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: bool); Signal::Success(Value { @@ -143,7 +143,7 @@ mod tests { } // dummy `run` closure (unused by these handlers) - fn dummy_run(_: i64, _: &mut ValueStore) -> Signal { + fn dummy_run(_: &crate::handler::argument::Thunk, _: &mut ValueStore) -> Signal { Signal::Success(Value { kind: Some(Kind::BoolValue(true)), }) diff --git a/crates/taurus-core/src/runtime/functions/control.rs b/crates/taurus-core/src/runtime/functions/control.rs index 6cb8a15..94954df 100644 --- a/crates/taurus-core/src/runtime/functions/control.rs +++ b/crates/taurus-core/src/runtime/functions/control.rs @@ -24,7 +24,7 @@ pub(crate) const FUNCTIONS: &[FunctionRegistration] = &[ fn stop( _args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { Signal::Stop } @@ -32,7 +32,7 @@ fn stop( fn value( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: Value); Signal::Success(value) @@ -41,7 +41,7 @@ fn value( fn r#return( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: Value); // The executor decides how far this return unwinds (one frame). @@ -51,7 +51,7 @@ fn r#return( fn r#if( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let [ Argument::Eval(Value { @@ -70,7 +70,7 @@ fn r#if( if *bool { // Branch execution is delegated to the executor through `run`. ctx.push_runtime_trace_label("branch=if".to_string()); - run(*if_pointer, ctx) + run(if_pointer, ctx) } else { Signal::Success(Value { kind: Some(Kind::NullValue(0)), @@ -81,7 +81,7 @@ fn r#if( fn if_else( args: &[Argument], ctx: &mut ValueStore, - run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { let [ Argument::Eval(Value { @@ -100,9 +100,9 @@ fn if_else( if *bool { ctx.push_runtime_trace_label("branch=if".to_string()); - run(*if_pointer, ctx) + run(if_pointer, ctx) } else { ctx.push_runtime_trace_label("branch=else".to_string()); - run(*else_pointer, ctx) + run(else_pointer, ctx) } } diff --git a/crates/taurus-core/src/runtime/functions/http.rs b/crates/taurus-core/src/runtime/functions/http.rs index 2ca6106..d88e76f 100644 --- a/crates/taurus-core/src/runtime/functions/http.rs +++ b/crates/taurus-core/src/runtime/functions/http.rs @@ -32,7 +32,7 @@ fn fail(category: &str, message: impl Into) -> Signal { fn respond( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => struct_val: Struct); @@ -93,7 +93,7 @@ fn respond( fn create_request( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => http_method: String, headers: Struct, http_url: String, payload: Value); let mut fields = std::collections::HashMap::new(); @@ -116,7 +116,7 @@ fn create_request( fn send_request( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => http_request: Struct); @@ -247,7 +247,7 @@ fn send_request( fn create_response( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => http_status_code: i64, headers: Struct, payload: Value); let mut fields = std::collections::HashMap::new(); @@ -731,7 +731,7 @@ mod tests { kind: Some(Kind::StructValue(request)), })]; let mut ctx = ValueStore::default(); - let mut run = |_: i64, _: &mut ValueStore| Signal::Stop; + let mut run = |_: &crate::handler::argument::Thunk, _: &mut ValueStore| Signal::Stop; let signal = send_request(&args, &mut ctx, &mut run); diff --git a/crates/taurus-core/src/runtime/functions/number.rs b/crates/taurus-core/src/runtime/functions/number.rs index 58c7346..05782a8 100644 --- a/crates/taurus-core/src/runtime/functions/number.rs +++ b/crates/taurus-core/src/runtime/functions/number.rs @@ -73,7 +73,7 @@ pub(crate) const FUNCTIONS: &[FunctionRegistration] = &[ fn has_digits( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); @@ -93,7 +93,7 @@ fn has_digits( fn remove_digits( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); match number_to_i64_lossy(&value) { @@ -109,7 +109,7 @@ fn remove_digits( fn add( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); // Preserve integer precision and overflow checks when both operands are integers. @@ -133,7 +133,7 @@ fn add( fn multiply( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); if let (Some(number_value::Number::Integer(a)), Some(number_value::Number::Integer(b))) = @@ -156,7 +156,7 @@ fn multiply( fn subtract( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); if let (Some(number_value::Number::Integer(a)), Some(number_value::Number::Integer(b))) = @@ -179,7 +179,7 @@ fn subtract( fn divide( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); @@ -214,7 +214,7 @@ fn divide( fn modulo( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); @@ -248,7 +248,7 @@ fn modulo( fn abs( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); if let Some(number_value::Number::Integer(i)) = value.number @@ -266,7 +266,7 @@ fn abs( fn is_positive( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -281,7 +281,7 @@ fn is_positive( fn is_greater( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); let lhs = match num_f64(&lhs) { @@ -300,7 +300,7 @@ fn is_greater( fn is_less( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); let lhs = match num_f64(&lhs) { @@ -319,7 +319,7 @@ fn is_less( fn is_zero( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -334,7 +334,7 @@ fn is_zero( fn square( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); if let Some(number_value::Number::Integer(i)) = value.number @@ -352,7 +352,7 @@ fn square( fn exponential( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => base: NumberValue, exponent: NumberValue); match (base.number, exponent.number) { @@ -381,7 +381,7 @@ fn exponential( fn pi( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { no_args!(args); Signal::Success(value_from_f64(f64::consts::PI)) @@ -390,7 +390,7 @@ fn pi( fn euler( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { no_args!(args); Signal::Success(value_from_f64(f64::consts::E)) @@ -399,7 +399,7 @@ fn euler( fn infinity( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { no_args!(args); Signal::Success(value_from_f64(f64::INFINITY)) @@ -408,7 +408,7 @@ fn infinity( fn round_up( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue, decimal_places: NumberValue); let decimal_places = match num_f64(&decimal_places) { @@ -432,7 +432,7 @@ fn round_up( fn round_down( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue, decimal_places: NumberValue); let decimal_places = match num_f64(&decimal_places) { @@ -456,7 +456,7 @@ fn round_down( fn round( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue, decimal_places: NumberValue); let decimal_places = match num_f64(&decimal_places) { @@ -480,7 +480,7 @@ fn round( fn square_root( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -493,7 +493,7 @@ fn square_root( fn root( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue, root: NumberValue); let value = match num_f64(&value) { @@ -510,7 +510,7 @@ fn root( fn log( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue, base: NumberValue); let value = match num_f64(&value) { @@ -556,7 +556,7 @@ fn log( fn ln( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -569,7 +569,7 @@ fn ln( fn from_text( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => string_value: String); @@ -589,7 +589,7 @@ fn from_text( fn as_text( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -604,7 +604,7 @@ fn as_text( fn min( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); if let (Some(number_value::Number::Integer(a)), Some(number_value::Number::Integer(b))) = @@ -626,7 +626,7 @@ fn min( fn max( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); if let (Some(number_value::Number::Integer(a)), Some(number_value::Number::Integer(b))) = @@ -648,7 +648,7 @@ fn max( fn negate( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); if let Some(number_value::Number::Integer(i)) = value.number @@ -666,7 +666,7 @@ fn negate( fn random( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => min: NumberValue, max: NumberValue); @@ -695,7 +695,7 @@ fn random( fn sin( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -708,7 +708,7 @@ fn sin( fn cos( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -721,7 +721,7 @@ fn cos( fn tan( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -734,7 +734,7 @@ fn tan( fn arcsin( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -747,7 +747,7 @@ fn arcsin( fn arccos( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -760,7 +760,7 @@ fn arccos( fn arctan( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -773,7 +773,7 @@ fn arctan( fn sinh( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -786,7 +786,7 @@ fn sinh( fn cosh( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue); let value = match num_f64(&value) { @@ -799,7 +799,7 @@ fn cosh( fn clamp( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: NumberValue, min: NumberValue, max: NumberValue); if let ( @@ -828,7 +828,7 @@ fn clamp( fn is_equal( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: NumberValue, rhs: NumberValue); let lhs = match num_f64(&lhs) { @@ -904,8 +904,8 @@ mod tests { } } - // dummy runner for handlers that accept `run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal` - fn dummy_run(_: i64, _: &mut ValueStore) -> Signal { + // dummy runner for handlers that accept `run: &mut crate::handler::registry::ThunkRunner<'_>` + fn dummy_run(_: &crate::handler::argument::Thunk, _: &mut ValueStore) -> Signal { Signal::Success(Value { kind: Some(Kind::NullValue(0)), }) diff --git a/crates/taurus-core/src/runtime/functions/object.rs b/crates/taurus-core/src/runtime/functions/object.rs index 0986ef4..283b629 100644 --- a/crates/taurus-core/src/runtime/functions/object.rs +++ b/crates/taurus-core/src/runtime/functions/object.rs @@ -22,7 +22,7 @@ pub(crate) const FUNCTIONS: &[FunctionRegistration] = &[ fn get( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => object: Struct, key: String); match object.fields.get(&key) { @@ -38,7 +38,7 @@ fn get( fn contains_key( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => object: Struct, key: String); let contains = object.fields.contains_key(&key); @@ -51,7 +51,7 @@ fn contains_key( fn size( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => object: Struct); Signal::Success(value_from_i64(object.fields.len() as i64)) @@ -60,7 +60,7 @@ fn size( fn keys( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => object: Struct); @@ -83,7 +83,7 @@ fn keys( fn set( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => object: Struct, key: String, value: Value); let mut new_object = object.clone(); @@ -159,8 +159,8 @@ mod tests { }) } - // dummy runner for handlers that accept `run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal` - fn dummy_run(_: i64, _: &mut ValueStore) -> Signal { + // dummy runner for handlers that accept `run: &mut crate::handler::registry::ThunkRunner<'_>` + fn dummy_run(_: &crate::handler::argument::Thunk, _: &mut ValueStore) -> Signal { Signal::Success(Value { kind: Some(Kind::NullValue(0)), }) diff --git a/crates/taurus-core/src/runtime/functions/text.rs b/crates/taurus-core/src/runtime/functions/text.rs index 383642b..8b44d98 100644 --- a/crates/taurus-core/src/runtime/functions/text.rs +++ b/crates/taurus-core/src/runtime/functions/text.rs @@ -59,7 +59,7 @@ fn arg_err>(msg: S) -> Signal { fn as_bytes( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -77,7 +77,7 @@ fn as_bytes( fn byte_size( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); Signal::Success(value_from_i64(value.len() as i64)) @@ -86,7 +86,7 @@ fn byte_size( fn capitalize( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -113,7 +113,7 @@ fn capitalize( fn uppercase( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); Signal::Success(Value { @@ -124,7 +124,7 @@ fn uppercase( fn lowercase( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); Signal::Success(Value { @@ -135,7 +135,7 @@ fn lowercase( fn swapcase( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -160,7 +160,7 @@ fn swapcase( fn trim( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); Signal::Success(Value { @@ -171,7 +171,7 @@ fn trim( fn chars( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -190,7 +190,7 @@ fn chars( fn at( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, index: tucana::shared::NumberValue); let index = match number_to_i64_lossy(&index) { @@ -222,7 +222,7 @@ fn at( fn append( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, suffix: String); Signal::Success(Value { @@ -233,7 +233,7 @@ fn append( fn prepend( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, prefix: String); Signal::Success(Value { @@ -244,7 +244,7 @@ fn prepend( fn insert( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, position: tucana::shared::NumberValue, text: String); let position = match number_to_i64_lossy(&position) { @@ -277,7 +277,7 @@ fn insert( fn length( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); Signal::Success(value_from_i64(value.chars().count() as i64)) @@ -286,7 +286,7 @@ fn length( fn remove( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, from: tucana::shared::NumberValue, to: tucana::shared::NumberValue); let from = match number_to_i64_lossy(&from) { @@ -334,7 +334,7 @@ fn remove( fn replace( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, old: String, new: String); let replaced = value.replace(&old, &new); @@ -346,7 +346,7 @@ fn replace( fn replace_first( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, old: String, new: String); let replaced = value.replacen(&old, &new, 1); @@ -358,7 +358,7 @@ fn replace_first( fn replace_last( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, old: String, new: String); @@ -384,7 +384,7 @@ fn replace_last( fn hex( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -402,7 +402,7 @@ fn hex( fn octal( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -420,7 +420,7 @@ fn octal( fn index_of( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, sub: String); @@ -433,7 +433,7 @@ fn index_of( fn contains( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, sub: String); Signal::Success(Value { @@ -444,7 +444,7 @@ fn contains( fn split( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, delimiter: String); @@ -463,7 +463,7 @@ fn split( fn reverse( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -476,7 +476,7 @@ fn reverse( fn starts_with( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, prefix: String); Signal::Success(Value { @@ -487,7 +487,7 @@ fn starts_with( fn ends_with( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, suffix: String); Signal::Success(Value { @@ -498,7 +498,7 @@ fn ends_with( fn to_ascii( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String); @@ -515,7 +515,7 @@ fn to_ascii( fn from_ascii( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { // Requires a TryFromArg impl for ListValue in your macro system. args!(args => list: ListValue); @@ -546,7 +546,7 @@ fn from_ascii( fn encode( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, encoding: String); @@ -565,7 +565,7 @@ fn encode( fn decode( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => value: String, encoding: String); @@ -600,7 +600,7 @@ fn decode( fn is_equal( args: &[Argument], _ctx: &mut ValueStore, - _run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal, + _run: &mut crate::handler::registry::ThunkRunner<'_>, ) -> Signal { args!(args => lhs: String, rhs: String); Signal::Success(Value { @@ -674,8 +674,8 @@ mod tests { } } - // dummy runner for handlers that accept `run: &mut dyn FnMut(i64, &mut ValueStore) -> Signal` - fn dummy_run(_: i64, _: &mut ValueStore) -> Signal { + // dummy runner for handlers that accept `run: &mut crate::handler::registry::ThunkRunner<'_>` + fn dummy_run(_: &crate::handler::argument::Thunk, _: &mut ValueStore) -> Signal { Signal::Success(Value { kind: Some(Kind::NullValue(0)), }) diff --git a/crates/taurus-tests/src/main.rs b/crates/taurus-tests/src/main.rs index 2cf79d0..db6ffd2 100644 --- a/crates/taurus-tests/src/main.rs +++ b/crates/taurus-tests/src/main.rs @@ -132,7 +132,7 @@ impl Testable for Case { flow_input, None, None, - true, + false, ); match res { diff --git a/docs/errors.md b/docs/errors.md index 72aa2dc..1b2a1ca 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -30,7 +30,7 @@ This document is the canonical catalog for runtime error codes emitted by Taurus | `T-CORE-000103` | Compiler | Flow compilation failed because a `next` edge points to a missing node. | `next_node_id` references unknown node id. | `runtime/engine/compiler.rs` | | `T-CORE-000104` | Compiler | Flow compilation failed because a parameter is structurally incomplete. | Parameter has no value payload in IR. | `runtime/engine/compiler.rs` | | `T-CORE-000105` | Compiler | Flow compilation failed because a sub-flow parameter is missing its execution reference. | `sub_flow.execution_reference` is absent. | `runtime/engine/compiler.rs` | -| `T-CORE-000106` | Compiler | Flow compilation failed because a sub-flow parameter uses an unsupported execution reference. | `sub_flow.execution_reference` is not a starting node id. | `runtime/engine/compiler.rs` | +| `T-CORE-000107` | Engine | Function sub-flow execution failed because a required setting value is missing. | A non-optional sub-flow setting has no callback input value and no default value. | `runtime/engine/executor.rs` | | `T-CORE-000201` | Handler | Handler argument arity contract was violated before function execution began. | `args!`/`no_args!` macro expected different argument count. | `handler/macros.rs` | | `T-CORE-000202` | Handler | Handler argument type conversion failed during typed extraction. | `TryFromArgument` expected type does not match provided argument. | `handler/argument.rs` | | `T-CORE-000301` | App Error Mapping | Application configuration failure mapped into runtime error format. | Invalid/missing runtime config surfaced as `Error::Configuration`. | `types/errors/error.rs` | diff --git a/flows/11_function_subflow.json b/flows/11_function_subflow.json new file mode 100644 index 0000000..53d8e61 --- /dev/null +++ b/flows/11_function_subflow.json @@ -0,0 +1,74 @@ +{ + "name": "11_function_subflow", + "description": "This flow validates sub_flow execution by function identifier with settings and defaults", + "inputs": [ + { + "input": null, + "expected_result": [ + 3, + 4 + ] + } + ], + "flow": { + "startingNodeId": "1", + "nodeFunctions": [ + { + "definition_source": "taurus", + "databaseId": "1", + "runtimeFunctionId": "std::list::map", + "parameters": [ + { + "databaseId": "100", + "runtimeParameterId": "list", + "value": { + "literalValue": { + "listValue": { + "values": [ + { + "numberValue": { + "integer": "1" + } + }, + { + "numberValue": { + "integer": "2" + } + } + ] + } + } + } + }, + { + "databaseId": "101", + "runtimeParameterId": "transform", + "value": { + "sub_flow": { + "signature": "", + "settings": [ + { + "identifier": "lhs", + "optional": false, + "hidden": false + }, + { + "identifier": "rhs", + "defaultValue": { + "numberValue": { + "integer": "2" + } + }, + "optional": false, + "hidden": true + } + ], + "functionIdentifier": "std::number::add" + } + } + } + ] + } + ] + } +}