From 05f7c519adaa7a1ee07f52e05c0fdb220717d570 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Mon, 13 Oct 2025 11:35:02 +0200 Subject: [PATCH 01/10] Add sleep node to flow engine Enables pausing execution within a flow for a specified duration. This introduces a new `Sleep` node type in the flow engine, allowing for timed delays during flow execution. The duration is specified in the flow definition using human-readable format, leveraging the `humantime-serde` crate for easy parsing. --- Cargo.lock | 17 ++++++++++++ Cargo.toml | 1 + src/flow_engine/flow.rs | 2 ++ src/flow_loader/factory.rs | 22 +++++++++++++++ src/flow_loader/serialized_flow.rs | 40 +++++++++++++++++++++++++--- tests/resources/flows/sleepFlow.json | 20 ++++++++++++++ 6 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 tests/resources/flows/sleepFlow.json diff --git a/Cargo.lock b/Cargo.lock index 80dd51c..a7ec097 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,6 +788,7 @@ dependencies = [ "cron", "ctor", "futures", + "humantime-serde", "mockito", "pretty_assertions", "reqwest", @@ -849,6 +850,22 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 1c34f40..03e142b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ thiserror = "2.0" async-trait = "0.1" cron = "0.15.0" chrono = "0.4.42" +humantime-serde = "1.1.1" # Action macros action_macros = { path = "action_macros" } diff --git a/src/flow_engine/flow.rs b/src/flow_engine/flow.rs index d0aebae..3282d36 100644 --- a/src/flow_engine/flow.rs +++ b/src/flow_engine/flow.rs @@ -3,6 +3,7 @@ use crate::flow_engine::action::Action; use crate::flow_engine::{Expression, Value}; use std::fmt::Debug; use std::sync::Arc; +use std::time::Duration; #[derive(Debug)] pub struct Flow { @@ -89,6 +90,7 @@ pub enum FlowNodeKind { Start, End, Action(ActionFlowNode), + Sleep(Duration), } #[derive(Debug)] diff --git a/src/flow_loader/factory.rs b/src/flow_loader/factory.rs index de70466..c3e38ec 100644 --- a/src/flow_loader/factory.rs +++ b/src/flow_loader/factory.rs @@ -32,6 +32,7 @@ pub fn from_json(json: &str) -> Result { SerializedFlowNode::StartNode(node) => node.outgoing_node == serialized_node.id(), SerializedFlowNode::EndNode(_) => false, SerializedFlowNode::ActionNode(node) => node.outgoing_node == serialized_node.id(), + SerializedFlowNode::SleepNode(node) => node.outgoing_node == serialized_node.id(), }); if !matches!(serialized_node, SerializedFlowNode::StartNode(_)) && incoming_nodes.is_empty() { @@ -86,6 +87,7 @@ fn to_flow_node(serialized_node: SerializedFlowNode, outgoing_nodes: Vec FlowNode::new(node.id, outgoing_nodes, FlowNodeKind::Start), SerializedFlowNode::EndNode(node) => FlowNode::new(node.id, outgoing_nodes, FlowNodeKind::End), SerializedFlowNode::ActionNode(node) => FlowNode::new(node.id, outgoing_nodes, FlowNodeKind::Action(ActionFlowNode::new(node.action))), + SerializedFlowNode::SleepNode(node) => FlowNode::new(node.id, outgoing_nodes, FlowNodeKind::Sleep(node.duration)), } } @@ -138,6 +140,7 @@ mod tests { use crate::flow_engine::action::{ControlDeviceAction, LogAction}; use crate::flow_engine::property_value::PropertyValue::SetBooleanValue; use pretty_assertions::assert_eq; + use std::time::Duration; #[tokio::test] async fn returns_an_error_if_an_unknown_node_type_is_found() { @@ -233,4 +236,23 @@ mod tests { let expected = Flow::new("controlDeviceFlow".to_string(), None, None, start_node).unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } + + #[tokio::test] + async fn creates_a_flow_with_a_sleep_node() { + let json = include_str!("../../tests/resources/flows/sleepFlow.json"); + let flow = from_json(json).unwrap(); + + let end_node = FlowNode::new("endNode".to_string(), vec![], FlowNodeKind::End); + + let sleep_node = FlowNode::new( + "sleepNode".to_string(), + vec![FlowLink::new(Arc::new(end_node), None)], + FlowNodeKind::Sleep(Duration::from_secs(3907)), + ); + + let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(sleep_node), None)], FlowNodeKind::Start); + + let expected = Flow::new("sleepFlow".to_string(), None, None, start_node).unwrap(); + assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); + } } diff --git a/src/flow_loader/serialized_flow.rs b/src/flow_loader/serialized_flow.rs index 9ec8160..eb9f867 100644 --- a/src/flow_loader/serialized_flow.rs +++ b/src/flow_loader/serialized_flow.rs @@ -1,6 +1,7 @@ use crate::flow_engine::Expression; use crate::flow_engine::action::Action; use serde::Deserialize; +use std::time::Duration; #[derive(Debug, Deserialize)] pub struct SerializedFlow { @@ -16,6 +17,7 @@ pub enum SerializedFlowNode { StartNode(SerializedStartFlowNode), EndNode(SerializedEndFlowNode), ActionNode(SerializedActionFlowNode), + SleepNode(SerializedSleepFlowNode), } impl SerializedFlowNode { @@ -24,6 +26,7 @@ impl SerializedFlowNode { SerializedFlowNode::StartNode(node) => &node.id, SerializedFlowNode::EndNode(node) => &node.id, SerializedFlowNode::ActionNode(node) => &node.id, + SerializedFlowNode::SleepNode(node) => &node.id, } } @@ -32,6 +35,7 @@ impl SerializedFlowNode { SerializedFlowNode::StartNode(node) => Some(&node.outgoing_node), SerializedFlowNode::EndNode(_) => None, SerializedFlowNode::ActionNode(node) => Some(&node.outgoing_node), + SerializedFlowNode::SleepNode(node) => Some(&node.outgoing_node), } } } @@ -56,6 +60,15 @@ pub struct SerializedActionFlowNode { pub(crate) action: Box, } +#[derive(Debug, Deserialize)] +#[serde(tag = "type", rename_all = "camelCase")] +pub struct SerializedSleepFlowNode { + pub(crate) id: String, + pub(crate) outgoing_node: String, + #[serde(with = "humantime_serde")] + pub(crate) duration: Duration, +} + #[cfg(test)] mod tests { use super::*; @@ -110,11 +123,30 @@ mod tests { } #[tokio::test] - async fn test_serialized_flow_with_schedule() { - let json = include_str!("../../tests/resources/flows/logFlowWithSchedule.json"); + async fn test_serialized_flow_with_sleep_node() { + let json = include_str!("../../tests/resources/flows/sleepFlow.json"); let flow = serde_json::from_str::(json).unwrap(); - assert_eq!(flow.schedule, Some("*/5 * * * * *".to_string())); - assert_eq!(flow.trigger, None); + let expected = SerializedFlow { + name: "sleepFlow".to_string(), + schedule: None, + trigger: None, + nodes: vec![ + SerializedFlowNode::StartNode(SerializedStartFlowNode { + id: "startNode".to_string(), + outgoing_node: "sleepNode".to_string(), + }), + SerializedFlowNode::SleepNode(SerializedSleepFlowNode { + id: "sleepNode".to_string(), + outgoing_node: "endNode".to_string(), + duration: Duration::from_secs(3907), + }), + SerializedFlowNode::EndNode(SerializedEndFlowNode { id: "endNode".to_string() }), + ], + }; + + // As ActionFlowNode's action cannot implement PartialEq, use debug print for comparison + assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); + println!("{:?}", flow); } } diff --git a/tests/resources/flows/sleepFlow.json b/tests/resources/flows/sleepFlow.json new file mode 100644 index 0000000..55b6d35 --- /dev/null +++ b/tests/resources/flows/sleepFlow.json @@ -0,0 +1,20 @@ +{ + "name": "sleepFlow", + "nodes": [ + { + "id": "startNode", + "type": "startNode", + "outgoingNode": "sleepNode" + }, + { + "id": "sleepNode", + "type": "sleepNode", + "outgoingNode": "endNode", + "duration": "1h5m7s" + }, + { + "id": "endNode", + "type": "endNode" + } + ] +} From ca570fc6a7698975cb30dfdd2fbcff22fb3832a0 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Wed, 15 Oct 2025 10:55:55 +0200 Subject: [PATCH 02/10] Schedule flows for sleep nodes Introduces the ability to schedule flows to continue execution after a specified duration when encountering a sleep node. This is achieved by sending a `SchedulerCommand::ScheduleOnce` to the scheduler, which then triggers the flow execution to resume at the designated node after the delay. This change also modifies the store listener and the main function to pass the scheduler transmitter to the `execute_flows` function, allowing scheduled executions from reactive flows. --- src/execute_flows.rs | 6 ++- src/flow_engine/engine.rs | 78 ++++++++++++++++++++++++++++++++++---- src/main.rs | 5 ++- src/scheduler/scheduler.rs | 15 ++++++-- src/store_listener.rs | 6 ++- 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/src/execute_flows.rs b/src/execute_flows.rs index 0ef0e20..8064677 100644 --- a/src/execute_flows.rs +++ b/src/execute_flows.rs @@ -4,19 +4,21 @@ use crate::flow_engine; use crate::flow_engine::flow::Flow; use crate::flow_engine::property_value::PropertyValue; use crate::flow_engine::{Context, FlowEngineError, FlowExecutionReport}; +use crate::scheduler::SchedulerCommand; use crate::store::StoreSnapshot; use futures::stream::FuturesUnordered; use futures::stream::StreamExt; use std::collections::HashMap; use std::sync::Arc; +use tokio::sync::mpsc::Sender; use tracing::{instrument, warn}; type CommandMap = HashMap>; #[instrument(skip_all)] -pub async fn execute_flows(flows: &[Flow], snapshot: StoreSnapshot) { +pub async fn execute_flows(flows: &[Flow], snapshot: StoreSnapshot, tx: Sender) { let context = Context::new(snapshot.clone()); - let results = FuturesUnordered::from_iter(flows.iter().map(|flow| async { flow_engine::execute(flow, &context).await })) + let results = FuturesUnordered::from_iter(flows.iter().map(|flow| async { flow_engine::execute(flow, &context, tx.clone()).await })) .collect::>() .await; diff --git a/src/flow_engine/engine.rs b/src/flow_engine/engine.rs index b690e4d..b12c444 100644 --- a/src/flow_engine/engine.rs +++ b/src/flow_engine/engine.rs @@ -3,15 +3,19 @@ use crate::flow_engine::context::Context; use crate::flow_engine::expression::{ExpressionError, evaluate}; use crate::flow_engine::flow::{Flow, FlowNode, FlowNodeKind}; use crate::flow_engine::scope::Scope; +use crate::scheduler::SchedulerCommand; +use ExecuteNodeResult::*; use std::any::Any; use std::collections::HashMap; use std::time::Duration; use thiserror::Error; +use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::error::SendError; use tokio::time::Instant; use tracing::{debug, info, instrument, trace, warn}; #[instrument(fields(flow = flow.name()), skip_all)] -pub async fn execute(flow: &Flow, context: &Context) -> Result { +pub async fn execute(flow: &Flow, context: &Context, tx: Sender) -> Result { debug!("⚖️ Evaluating trigger condition for flow..."); let result = evaluate(flow.trigger(), context); match result { @@ -32,7 +36,19 @@ pub async fn execute(flow: &Flow, context: &Context) -> Result Some(node), + End => None, + Sleep { duration, next } => { + tx.send(SchedulerCommand::ScheduleOnce { + flow_name: flow.name().to_string(), + node_id: next.id().to_string(), + delay: duration, + }) + .await?; + None + } + } } let duration = Instant::now() - start; @@ -42,7 +58,7 @@ pub async fn execute(flow: &Flow, context: &Context) -> Result(node: &'a FlowNode, context: &Context, scope: &mut Scope) -> Result, FlowEngineError> { +async fn execute_node<'a>(node: &'a FlowNode, context: &Context, scope: &mut Scope) -> Result, FlowEngineError> { trace!("{:?}", node); let next_flow_link = match node.kind() { @@ -57,18 +73,34 @@ async fn execute_node<'a>(node: &'a FlowNode, context: &Context, scope: &mut Sco let next_node = next_flow_link.ok_or_else(|| FlowEngineError::MissingOutgoingNode(node.id().to_owned()))?.node(); debug!("Next node: {}", next_node.id()); + + if let FlowNodeKind::Sleep(duration) = node.kind() { + return Ok(Sleep { + duration: duration.clone(), + next: next_node, + }); + } + match next_node.kind() { - FlowNodeKind::End => Ok(None), - _ => Ok(Some(next_node)), + FlowNodeKind::End => Ok(End), + _ => Ok(Next(next_node)), } } +enum ExecuteNodeResult<'a> { + Next(&'a FlowNode), + End, + Sleep { duration: Duration, next: &'a FlowNode }, +} + #[derive(Error, Debug)] pub enum FlowEngineError { #[error("missing outgoing node for node '{0}'")] MissingOutgoingNode(String), #[error("evaluation of the flow trigger failed: {0}")] FailedTriggerEvaluation(ExpressionError), + #[error(transparent)] + FailedScheduleSleepCommand(#[from] SendError), } pub struct FlowExecutionReport { @@ -110,6 +142,7 @@ mod tests { use crate::flow_engine::flow::{ActionFlowNode, FlowLink, FlowNodeKind}; use std::sync::Arc; use test_log::test; + use tokio::sync::mpsc; #[test(tokio::test)] async fn executes_a_flow_with_one_action_node() { @@ -125,7 +158,8 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(log_node), None)], FlowNodeKind::Start); let flow = Flow::new("flow".to_string(), None, None, start_node).unwrap(); - let result = execute(&flow, &Context::default()).await; + let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); + let result = execute(&flow, &Context::default(), scheduler_tx).await; assert!(result.is_ok()); } @@ -134,7 +168,8 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![], FlowNodeKind::Start); let flow = Flow::new("flow".to_string(), None, Some(Expression::Literal { value: Value::Boolean(false) }), start_node).unwrap(); - let result = execute(&flow, &Context::default()).await; + let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); + let result = execute(&flow, &Context::default(), scheduler_tx).await; assert!(result.is_ok()); let result = result.unwrap(); assert!(result.scope.is_empty()); @@ -146,7 +181,34 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![], FlowNodeKind::Start); let flow = Flow::new("flow".to_string(), None, None, start_node).unwrap(); - let result = execute(&flow, &Context::default()).await; + let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); + let result = execute(&flow, &Context::default(), scheduler_tx).await; assert!(matches!(result, Err(FlowEngineError::MissingOutgoingNode(_)))); } + + #[test(tokio::test)] + async fn sends_a_schedule_once_command_for_a_sleep_node() { + let end_node = FlowNode::new("end_node".to_string(), vec![], FlowNodeKind::End); + + let sleep_node = FlowNode::new( + "sleep_node".to_string(), + vec![FlowLink::new(Arc::new(end_node), None)], + FlowNodeKind::Sleep(Duration::from_secs(42)), + ); + + let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(sleep_node), None)], FlowNodeKind::Start); + let flow = Flow::new("flow".to_string(), None, None, start_node).unwrap(); + + let (scheduler_tx, mut scheduler_rx) = mpsc::channel::(32); + + execute(&flow, &Context::default(), scheduler_tx).await.unwrap(); + let received_command = scheduler_rx.recv().await; + if let Some(SchedulerCommand::ScheduleOnce { flow_name, node_id, delay }) = received_command { + assert_eq!(flow_name, "flow"); + assert_eq!(node_id, "end_node"); + assert_eq!(delay, Duration::from_secs(42)); + } else { + panic!("Expected ScheduleOnce command"); + } + } } diff --git a/src/main.rs b/src/main.rs index bceb47b..98f3d5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,8 +40,9 @@ async fn main() -> Result<(), Box> { let (scheduler_tx, scheduler_rx) = mpsc::channel::(32); let store_rx = store.notifier(); + let scheduler_tx_clone = scheduler_tx.clone(); task::spawn(async move { - scheduler(scheduler_rx, store_rx).await; + scheduler(scheduler_tx_clone.clone(), scheduler_rx, store_rx).await; }); for scheduled_flow in scheduled_flows { scheduler_tx.send(SchedulerCommand::Schedule(scheduled_flow)).await?; @@ -55,7 +56,7 @@ async fn main() -> Result<(), Box> { let store_rx = store.notifier(); task::spawn(async move { - store_listener(store_rx, reactive_flows).await; + store_listener(store_rx, reactive_flows, scheduler_tx.clone()).await; }); info!("✅ Initialized store listener"); diff --git a/src/scheduler/scheduler.rs b/src/scheduler/scheduler.rs index af31c10..8f3c296 100644 --- a/src/scheduler/scheduler.rs +++ b/src/scheduler/scheduler.rs @@ -5,7 +5,7 @@ use chrono::Utc; use cron::Schedule; use std::str::FromStr; use std::time::Duration; -use tokio::sync::mpsc::Receiver; +use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::watch::Receiver as WatchReceiver; use tokio::time::{Instant, sleep_until}; use tracing::{debug, error, info, instrument, warn}; @@ -13,10 +13,11 @@ use tracing::{debug, error, info, instrument, warn}; #[derive(Debug)] pub enum SchedulerCommand { Schedule(Flow), + ScheduleOnce { flow_name: String, node_id: String, delay: Duration }, } #[instrument(skip_all)] -pub async fn scheduler(mut rx: Receiver, notifier_rx: WatchReceiver) { +pub async fn scheduler(tx: Sender, mut rx: Receiver, notifier_rx: WatchReceiver) { while let Some(cmd) = rx.recv().await { match cmd { SchedulerCommand::Schedule(flow) if flow.schedule().is_some() => { @@ -34,6 +35,7 @@ pub async fn scheduler(mut rx: Receiver, notifier_rx: WatchRec // Job loop let notifier_rx_clone = notifier_rx.clone(); + let tx_clone = tx.clone(); tokio::spawn(async move { for datetime in schedule.upcoming(Utc) { let duration = datetime.signed_duration_since(Utc::now()); @@ -46,7 +48,7 @@ pub async fn scheduler(mut rx: Receiver, notifier_rx: WatchRec debug!(cron, "🕗 Running scheduled flow '{}'...", flow.name()); let snapshot = notifier_rx_clone.borrow().clone(); - execute_flows(std::slice::from_ref(&flow), snapshot).await; + execute_flows(std::slice::from_ref(&flow), snapshot, tx_clone.clone()).await; } }); info!("🕗 Scheduling flow '{}'... OK", flow_name); @@ -54,6 +56,13 @@ pub async fn scheduler(mut rx: Receiver, notifier_rx: WatchRec SchedulerCommand::Schedule(flow) => { error!("🕗 Scheduling flow '{}'... failed, not a scheduled flow", flow.name()); } + SchedulerCommand::ScheduleOnce { + flow_name: flow, + node_id: node, + delay, + } => { + info!("🕗 Scheduling flow '{}' to run node '{}' after {:?}... OK", flow, node, delay); + } } } } diff --git a/src/store_listener.rs b/src/store_listener.rs index 036e67b..781a3ce 100644 --- a/src/store_listener.rs +++ b/src/store_listener.rs @@ -1,13 +1,15 @@ use crate::execute_flows::execute_flows; use crate::flow_engine::flow::Flow; +use crate::scheduler::SchedulerCommand; use crate::store::StoreSnapshot; +use tokio::sync::mpsc::Sender; use tokio::sync::watch::Receiver; use tracing::instrument; #[instrument(skip_all)] -pub async fn store_listener(mut rx: Receiver, flows: Vec) { +pub async fn store_listener(mut rx: Receiver, flows: Vec, scheduler_tx: Sender) { while rx.changed().await.is_ok() { let snapshot: StoreSnapshot = rx.borrow().clone(); - execute_flows(&flows, snapshot).await; + execute_flows(&flows, snapshot, scheduler_tx.clone()).await; } } From ce22c0256ba27934241cb71518834ed2865ca7f9 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Wed, 15 Oct 2025 11:15:55 +0200 Subject: [PATCH 03/10] Move scheduler to flow engine Moves the scheduler module from its own directory into the flow_engine directory. This change improves project organization and reflects that the scheduler is closely related and used by the flow engine. It also ensures that the flow engine has access to scheduling capabilities. This change also updates references to the scheduler in other modules. --- src/flow_engine/engine.rs | 3 +-- src/flow_engine/mod.rs | 2 ++ src/{scheduler => flow_engine}/scheduler.rs | 0 src/main.rs | 3 +-- src/scheduler/mod.rs | 3 --- 5 files changed, 4 insertions(+), 7 deletions(-) rename src/{scheduler => flow_engine}/scheduler.rs (100%) delete mode 100644 src/scheduler/mod.rs diff --git a/src/flow_engine/engine.rs b/src/flow_engine/engine.rs index b12c444..cc13bb1 100644 --- a/src/flow_engine/engine.rs +++ b/src/flow_engine/engine.rs @@ -1,9 +1,8 @@ -use crate::flow_engine::Value; use crate::flow_engine::context::Context; use crate::flow_engine::expression::{ExpressionError, evaluate}; use crate::flow_engine::flow::{Flow, FlowNode, FlowNodeKind}; use crate::flow_engine::scope::Scope; -use crate::scheduler::SchedulerCommand; +use crate::flow_engine::{SchedulerCommand, Value}; use ExecuteNodeResult::*; use std::any::Any; use std::collections::HashMap; diff --git a/src/flow_engine/mod.rs b/src/flow_engine/mod.rs index 69931fc..da8b531 100644 --- a/src/flow_engine/mod.rs +++ b/src/flow_engine/mod.rs @@ -5,6 +5,7 @@ mod engine; mod expression; pub mod flow; pub mod property_value; +pub mod scheduler; mod scope; pub use context::Context; @@ -12,3 +13,4 @@ pub use engine::FlowEngineError; pub use engine::FlowExecutionReport; pub use engine::execute; pub use expression::{Expression, Value}; +pub use scheduler::{SchedulerCommand, scheduler}; diff --git a/src/scheduler/scheduler.rs b/src/flow_engine/scheduler.rs similarity index 100% rename from src/scheduler/scheduler.rs rename to src/flow_engine/scheduler.rs diff --git a/src/main.rs b/src/main.rs index 98f3d5c..d2bba2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use crate::app_config::AppConfig; use crate::domain::controller_registry; use crate::domain::events::Event; -use crate::scheduler::{SchedulerCommand, scheduler}; +use crate::flow_engine::{SchedulerCommand, scheduler}; use crate::store::Store; use crate::store_listener::store_listener; use std::sync::Arc; @@ -17,7 +17,6 @@ mod flow_engine; mod flow_loader; mod hue; mod property_changed_reducer; -mod scheduler; mod sse; mod store; mod store_listener; diff --git a/src/scheduler/mod.rs b/src/scheduler/mod.rs deleted file mode 100644 index a7b94cf..0000000 --- a/src/scheduler/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod scheduler; - -pub use scheduler::{SchedulerCommand, scheduler}; From c31ddfa9860a47e686ce5e6b315de27d57d91040 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Wed, 15 Oct 2025 13:56:03 +0200 Subject: [PATCH 04/10] Add id to flow definition This commit introduces an `id` field to the `Flow` struct, enabling unique identification of flows. The `Flow::new` function now requires an `id` parameter, which is stored within the `Flow` struct. The flow loader and tests are updated to use the new `id` field. --- src/flow_engine/engine.rs | 15 +++++++++++---- src/flow_engine/flow.rs | 8 +++++++- src/flow_loader/factory.rs | 19 +++++++++++++------ src/flow_loader/serialized_flow.rs | 3 +++ tests/resources/flows/controlDeviceFlow.json | 1 + tests/resources/flows/emptyFlow.json | 1 + .../flows/invalid/missingEndNodeFlow.json | 1 + .../flows/invalid/multipleStartNodesFlow.json | 1 + .../flows/invalid/unconnectedNodeFlow.json | 1 + .../flows/invalid/unknownNodeTypeFlow.json | 1 + .../flows/invalid/unusedNodesFlow.json | 1 + tests/resources/flows/logFlow.json | 1 + .../resources/flows/logFlowWithSchedule.json | 1 + tests/resources/flows/logFlowWithTrigger.json | 1 + tests/resources/flows/sleepFlow.json | 1 + 15 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/flow_engine/engine.rs b/src/flow_engine/engine.rs index cc13bb1..c1f8a64 100644 --- a/src/flow_engine/engine.rs +++ b/src/flow_engine/engine.rs @@ -155,7 +155,7 @@ mod tests { ); let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(log_node), None)], FlowNodeKind::Start); - let flow = Flow::new("flow".to_string(), None, None, start_node).unwrap(); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node).unwrap(); let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); let result = execute(&flow, &Context::default(), scheduler_tx).await; @@ -165,7 +165,14 @@ mod tests { #[test(tokio::test)] async fn skips_execution_if_the_trigger_returns_false() { let start_node = FlowNode::new("startNode".to_string(), vec![], FlowNodeKind::Start); - let flow = Flow::new("flow".to_string(), None, Some(Expression::Literal { value: Value::Boolean(false) }), start_node).unwrap(); + let flow = Flow::new( + "id".to_string(), + "flow".to_string(), + None, + Some(Expression::Literal { value: Value::Boolean(false) }), + start_node, + ) + .unwrap(); let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); let result = execute(&flow, &Context::default(), scheduler_tx).await; @@ -178,7 +185,7 @@ mod tests { #[test(tokio::test)] async fn fails_if_an_outgoing_node_is_missing() { let start_node = FlowNode::new("startNode".to_string(), vec![], FlowNodeKind::Start); - let flow = Flow::new("flow".to_string(), None, None, start_node).unwrap(); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node).unwrap(); let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); let result = execute(&flow, &Context::default(), scheduler_tx).await; @@ -196,7 +203,7 @@ mod tests { ); let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(sleep_node), None)], FlowNodeKind::Start); - let flow = Flow::new("flow".to_string(), None, None, start_node).unwrap(); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node).unwrap(); let (scheduler_tx, mut scheduler_rx) = mpsc::channel::(32); diff --git a/src/flow_engine/flow.rs b/src/flow_engine/flow.rs index 3282d36..7f970e8 100644 --- a/src/flow_engine/flow.rs +++ b/src/flow_engine/flow.rs @@ -7,6 +7,7 @@ use std::time::Duration; #[derive(Debug)] pub struct Flow { + id: String, name: String, schedule: Option, trigger: Expression, @@ -14,9 +15,10 @@ pub struct Flow { } impl Flow { - pub fn new(name: String, schedule: Option, trigger: Option, start_node: FlowNode) -> Result { + pub fn new(id: String, name: String, schedule: Option, trigger: Option, start_node: FlowNode) -> Result { match start_node.kind { FlowNodeKind::Start => Ok(Flow { + id, name, schedule, trigger: trigger.unwrap_or(Literal { value: Value::Boolean(true) }), @@ -26,6 +28,10 @@ impl Flow { } } + pub fn id(&self) -> &str { + &self.id + } + pub fn name(&self) -> &str { &self.name } diff --git a/src/flow_loader/factory.rs b/src/flow_loader/factory.rs index c3e38ec..26278ec 100644 --- a/src/flow_loader/factory.rs +++ b/src/flow_loader/factory.rs @@ -64,7 +64,14 @@ pub fn from_json(json: &str) -> Result { }); } - let flow = Flow::new(flow.name, flow.schedule, flow.trigger, start_node.ok_or_else(|| FlowFactoryError::MissingStartNode)?).expect("Flow creation failed"); + let flow = Flow::new( + flow.id, + flow.name, + flow.schedule, + flow.trigger, + start_node.ok_or_else(|| FlowFactoryError::MissingStartNode)?, + ) + .expect("Flow creation failed"); Ok(flow) } @@ -151,7 +158,7 @@ mod tests { #[tokio::test] async fn returns_an_error_if_no_start_node_is_found() { - let json = r#"{ "name": "flow", "nodes": [] }"#; + let json = r#"{ "id": "id", "name": "flow", "nodes": [] }"#; let result = from_json(json); assert!(matches!(result, Err(FlowFactoryError::MissingStartNode))); } @@ -192,7 +199,7 @@ mod tests { let end_node = FlowNode::new("endNode".to_string(), vec![], FlowNodeKind::End); let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(end_node), None)], FlowNodeKind::Start); - let expected = Flow::new("emptyFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new("01K7KK65D87SZGGZE7VB8QYT20".to_string(), "emptyFlow".to_string(), None, None, start_node).unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } @@ -211,7 +218,7 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(action_node), None)], FlowNodeKind::Start); - let expected = Flow::new("logFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new("01K7KK6H5R7Y72QJEJSJQCKMRQ".to_string(), "logFlow".to_string(), None, None, start_node).unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } @@ -233,7 +240,7 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(action_node), None)], FlowNodeKind::Start); - let expected = Flow::new("controlDeviceFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new("01K7KK5FC54SN8D4QYVNEGFYG4".to_string(), "controlDeviceFlow".to_string(), None, None, start_node).unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } @@ -252,7 +259,7 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(sleep_node), None)], FlowNodeKind::Start); - let expected = Flow::new("sleepFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new("01K7KK7E6GG26XZZDXSGFZCWQ4".to_string(), "sleepFlow".to_string(), None, None, start_node).unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } } diff --git a/src/flow_loader/serialized_flow.rs b/src/flow_loader/serialized_flow.rs index eb9f867..afb86ef 100644 --- a/src/flow_loader/serialized_flow.rs +++ b/src/flow_loader/serialized_flow.rs @@ -5,6 +5,7 @@ use std::time::Duration; #[derive(Debug, Deserialize)] pub struct SerializedFlow { + pub(crate) id: String, pub(crate) name: String, pub(crate) schedule: Option, pub(crate) trigger: Option, @@ -83,6 +84,7 @@ mod tests { let flow = serde_json::from_str::(json).unwrap(); let expected = SerializedFlow { + id: "01K7KK6H5R7Y72QJEJSJQCKMRQ".to_string(), name: "logFlow".to_string(), schedule: None, trigger: None, @@ -128,6 +130,7 @@ mod tests { let flow = serde_json::from_str::(json).unwrap(); let expected = SerializedFlow { + id: "01K7KK7E6GG26XZZDXSGFZCWQ4".to_string(), name: "sleepFlow".to_string(), schedule: None, trigger: None, diff --git a/tests/resources/flows/controlDeviceFlow.json b/tests/resources/flows/controlDeviceFlow.json index 3cbf426..c6e2594 100644 --- a/tests/resources/flows/controlDeviceFlow.json +++ b/tests/resources/flows/controlDeviceFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KK5FC54SN8D4QYVNEGFYG4", "name": "controlDeviceFlow", "nodes": [ { diff --git a/tests/resources/flows/emptyFlow.json b/tests/resources/flows/emptyFlow.json index 1ec7b80..ad0a010 100644 --- a/tests/resources/flows/emptyFlow.json +++ b/tests/resources/flows/emptyFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KK65D87SZGGZE7VB8QYT20", "name": "emptyFlow", "nodes": [ { diff --git a/tests/resources/flows/invalid/missingEndNodeFlow.json b/tests/resources/flows/invalid/missingEndNodeFlow.json index 5b1c114..8d2105f 100644 --- a/tests/resources/flows/invalid/missingEndNodeFlow.json +++ b/tests/resources/flows/invalid/missingEndNodeFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KKNRMMQZCBRKMM914VK75R", "name": "emptyFlow", "nodes": [ { diff --git a/tests/resources/flows/invalid/multipleStartNodesFlow.json b/tests/resources/flows/invalid/multipleStartNodesFlow.json index 8f51256..fc0bded 100644 --- a/tests/resources/flows/invalid/multipleStartNodesFlow.json +++ b/tests/resources/flows/invalid/multipleStartNodesFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KKNG97SPZHAZEJYKBWRBNG", "name": "flow", "nodes": [ { diff --git a/tests/resources/flows/invalid/unconnectedNodeFlow.json b/tests/resources/flows/invalid/unconnectedNodeFlow.json index 846ab75..36012ba 100644 --- a/tests/resources/flows/invalid/unconnectedNodeFlow.json +++ b/tests/resources/flows/invalid/unconnectedNodeFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KKMWZQ7YNYKDCN9VT8YTCW", "name": "emptyFlow", "nodes": [ { diff --git a/tests/resources/flows/invalid/unknownNodeTypeFlow.json b/tests/resources/flows/invalid/unknownNodeTypeFlow.json index 656a81e..1bfa2b8 100644 --- a/tests/resources/flows/invalid/unknownNodeTypeFlow.json +++ b/tests/resources/flows/invalid/unknownNodeTypeFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KKQ3VZB85PE2HKA4PQWJTX", "name": "emptyFlow", "nodes": [ { diff --git a/tests/resources/flows/invalid/unusedNodesFlow.json b/tests/resources/flows/invalid/unusedNodesFlow.json index 3ea78ae..3c93aa0 100644 --- a/tests/resources/flows/invalid/unusedNodesFlow.json +++ b/tests/resources/flows/invalid/unusedNodesFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KKQAS625TBDHNCHW3TH5M2", "name": "emptyFlow", "nodes": [ { diff --git a/tests/resources/flows/logFlow.json b/tests/resources/flows/logFlow.json index c4c7fad..4efbf84 100644 --- a/tests/resources/flows/logFlow.json +++ b/tests/resources/flows/logFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KK6H5R7Y72QJEJSJQCKMRQ", "name": "logFlow", "nodes": [ { diff --git a/tests/resources/flows/logFlowWithSchedule.json b/tests/resources/flows/logFlowWithSchedule.json index 116c9fd..5a404cb 100644 --- a/tests/resources/flows/logFlowWithSchedule.json +++ b/tests/resources/flows/logFlowWithSchedule.json @@ -1,4 +1,5 @@ { + "id": "01K7KK6S7VH9ZMY2MXPD4NJJGC", "name": "logFlowWithSchedule", "schedule": "*/5 * * * * *", "nodes": [ diff --git a/tests/resources/flows/logFlowWithTrigger.json b/tests/resources/flows/logFlowWithTrigger.json index 09fc9e6..106c45c 100644 --- a/tests/resources/flows/logFlowWithTrigger.json +++ b/tests/resources/flows/logFlowWithTrigger.json @@ -1,4 +1,5 @@ { + "id": "01K7KK75QJTCKQFD9VYGEDA5P6", "name": "logFlowWithTrigger", "trigger": { "type": "equalTo", diff --git a/tests/resources/flows/sleepFlow.json b/tests/resources/flows/sleepFlow.json index 55b6d35..a1b436e 100644 --- a/tests/resources/flows/sleepFlow.json +++ b/tests/resources/flows/sleepFlow.json @@ -1,4 +1,5 @@ { + "id": "01K7KK7E6GG26XZZDXSGFZCWQ4", "name": "sleepFlow", "nodes": [ { From 4b6b962241bc21ed3c2ee632dc1bd8326e50dd24 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Wed, 15 Oct 2025 16:48:16 +0200 Subject: [PATCH 05/10] Introduce Flow Registry for flow management Adds a `FlowRegistry` to manage and retrieve flows, replacing direct vector access. This change improves flow organization and lookup efficiency, especially for scheduled flows. The scheduler now uses the registry to fetch flows by id, and the store listener uses it to access reactive flows. This centralizes flow management and reduces code duplication. --- src/execute_flows.rs | 2 +- src/flow_engine/scheduler.rs | 24 ++++++++++++++++-------- src/flow_registry.rs | 29 +++++++++++++++++++++++++++++ src/main.rs | 21 +++++++++++++++------ src/store_listener.rs | 7 ++++--- 5 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 src/flow_registry.rs diff --git a/src/execute_flows.rs b/src/execute_flows.rs index 8064677..3e55d11 100644 --- a/src/execute_flows.rs +++ b/src/execute_flows.rs @@ -16,7 +16,7 @@ use tracing::{instrument, warn}; type CommandMap = HashMap>; #[instrument(skip_all)] -pub async fn execute_flows(flows: &[Flow], snapshot: StoreSnapshot, tx: Sender) { +pub async fn execute_flows(flows: Vec>, snapshot: StoreSnapshot, tx: Sender) { let context = Context::new(snapshot.clone()); let results = FuturesUnordered::from_iter(flows.iter().map(|flow| async { flow_engine::execute(flow, &context, tx.clone()).await })) .collect::>() diff --git a/src/flow_engine/scheduler.rs b/src/flow_engine/scheduler.rs index 8f3c296..59207ca 100644 --- a/src/flow_engine/scheduler.rs +++ b/src/flow_engine/scheduler.rs @@ -1,9 +1,10 @@ use crate::execute_flows::execute_flows; -use crate::flow_engine::flow::Flow; +use crate::flow_registry::FlowRegistry; use crate::store::StoreSnapshot; use chrono::Utc; use cron::Schedule; use std::str::FromStr; +use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::watch::Receiver as WatchReceiver; @@ -12,18 +13,28 @@ use tracing::{debug, error, info, instrument, warn}; #[derive(Debug)] pub enum SchedulerCommand { - Schedule(Flow), + Schedule { flow_id: String }, ScheduleOnce { flow_name: String, node_id: String, delay: Duration }, } #[instrument(skip_all)] -pub async fn scheduler(tx: Sender, mut rx: Receiver, notifier_rx: WatchReceiver) { +pub async fn scheduler(tx: Sender, mut rx: Receiver, notifier_rx: WatchReceiver, flow_registry: Arc) { while let Some(cmd) = rx.recv().await { match cmd { - SchedulerCommand::Schedule(flow) if flow.schedule().is_some() => { + SchedulerCommand::Schedule { flow_id } => { + let Some(flow) = flow_registry.by_id(&flow_id) else { + warn!("🕗 Scheduling flow '{}'... failed, flow not found", flow_id); + continue; + }; + let flow_name = flow.name().to_string(); debug!("🕗 Scheduling flow '{}'...", flow_name); + if flow.schedule().is_none() { + error!("🕗 Scheduling flow '{}'... failed, not a scheduled flow", flow.name()); + continue; + } + let cron = flow.schedule().unwrap().to_string(); // Safe because of the match guard let schedule = match Schedule::from_str(&cron) { Ok(schedule) => schedule, @@ -48,14 +59,11 @@ pub async fn scheduler(tx: Sender, mut rx: Receiver { - error!("🕗 Scheduling flow '{}'... failed, not a scheduled flow", flow.name()); - } SchedulerCommand::ScheduleOnce { flow_name: flow, node_id: node, diff --git a/src/flow_registry.rs b/src/flow_registry.rs new file mode 100644 index 0000000..b47ad47 --- /dev/null +++ b/src/flow_registry.rs @@ -0,0 +1,29 @@ +use crate::flow_engine::flow::Flow; +use std::collections::HashMap; +use std::sync::Arc; + +pub struct FlowRegistry { + flows: Vec>, + by_id: HashMap, +} + +impl FlowRegistry { + pub fn new(flows: Vec) -> Self { + let by_id = flows.iter().enumerate().map(|(index, flow)| (flow.id().to_string(), index)).collect(); + let flow_arcs = flows.into_iter().map(|flow| Arc::new(flow)).collect(); + + Self { flows: flow_arcs, by_id } + } + + pub fn reactive_flows(&self) -> Vec> { + self.flows.iter().filter(|flow| flow.schedule().is_none()).cloned().collect() + } + + pub fn scheduled_flows(&self) -> Vec> { + self.flows.iter().filter(|flow| flow.schedule().is_some()).cloned().collect() + } + + pub fn by_id(&self, id: &str) -> Option> { + self.by_id.get(id).map(|&index| &self.flows[index]).cloned() + } +} diff --git a/src/main.rs b/src/main.rs index d2bba2f..bef5c12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use crate::app_config::AppConfig; use crate::domain::controller_registry; use crate::domain::events::Event; use crate::flow_engine::{SchedulerCommand, scheduler}; +use crate::flow_registry::FlowRegistry; use crate::store::Store; use crate::store_listener::store_listener; use std::sync::Arc; @@ -15,6 +16,7 @@ mod execute_flows; mod extensions; mod flow_engine; mod flow_loader; +mod flow_registry; mod hue; mod property_changed_reducer; mod sse; @@ -31,20 +33,27 @@ async fn main() -> Result<(), Box> { info!("✅ Loaded configuration"); let flows = flow_loader::load_flows_from(config.flows().directory(), "json").await.unwrap_or_else(|_| Vec::new()); // Errors are already logged in the function - let (scheduled_flows, reactive_flows): (Vec<_>, Vec<_>) = flows.into_iter().partition(|flow| flow.schedule().is_some()); + let flow_registry = Arc::new(FlowRegistry::new(flows)); info!("✅ Loaded flows"); let (tx, rx) = mpsc::channel::(config.core().store_buffer_size()); let mut store = Store::new(rx); let (scheduler_tx, scheduler_rx) = mpsc::channel::(32); - let store_rx = store.notifier(); let scheduler_tx_clone = scheduler_tx.clone(); + let store_rx = store.notifier(); + let registry_clone = flow_registry.clone(); task::spawn(async move { - scheduler(scheduler_tx_clone.clone(), scheduler_rx, store_rx).await; + scheduler(scheduler_tx_clone.clone(), scheduler_rx, store_rx, registry_clone).await; }); - for scheduled_flow in scheduled_flows { - scheduler_tx.send(SchedulerCommand::Schedule(scheduled_flow)).await?; + info!("✅ Started scheduler"); + + for scheduled_flow in flow_registry.scheduled_flows() { + scheduler_tx + .send(SchedulerCommand::Schedule { + flow_id: scheduled_flow.id().to_string(), + }) + .await?; } info!("✅ Scheduled flows"); @@ -55,7 +64,7 @@ async fn main() -> Result<(), Box> { let store_rx = store.notifier(); task::spawn(async move { - store_listener(store_rx, reactive_flows, scheduler_tx.clone()).await; + store_listener(store_rx, flow_registry, scheduler_tx.clone()).await; }); info!("✅ Initialized store listener"); diff --git a/src/store_listener.rs b/src/store_listener.rs index 781a3ce..92373df 100644 --- a/src/store_listener.rs +++ b/src/store_listener.rs @@ -1,15 +1,16 @@ use crate::execute_flows::execute_flows; -use crate::flow_engine::flow::Flow; +use crate::flow_registry::FlowRegistry; use crate::scheduler::SchedulerCommand; use crate::store::StoreSnapshot; +use std::sync::Arc; use tokio::sync::mpsc::Sender; use tokio::sync::watch::Receiver; use tracing::instrument; #[instrument(skip_all)] -pub async fn store_listener(mut rx: Receiver, flows: Vec, scheduler_tx: Sender) { +pub async fn store_listener(mut rx: Receiver, flow_registry: Arc, scheduler_tx: Sender) { while rx.changed().await.is_ok() { let snapshot: StoreSnapshot = rx.borrow().clone(); - execute_flows(&flows, snapshot, scheduler_tx.clone()).await; + execute_flows(flow_registry.reactive_flows(), snapshot, scheduler_tx.clone()).await; } } From ce419786143e19363b2b429ff6eee196a0596c26 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Wed, 15 Oct 2025 16:51:26 +0200 Subject: [PATCH 06/10] Remove unnecessary calls to clone Addresses issues where spawned tasks were incorrectly consuming variables. By cloning the `duration` field from the `Sleep` struct is unnecesary because `duration` implements the `Copy` trait and removing unnecessary clones of tx channels, it ensures that ownership is properly managed. --- src/flow_engine/engine.rs | 2 +- src/main.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flow_engine/engine.rs b/src/flow_engine/engine.rs index c1f8a64..2ffb42f 100644 --- a/src/flow_engine/engine.rs +++ b/src/flow_engine/engine.rs @@ -75,7 +75,7 @@ async fn execute_node<'a>(node: &'a FlowNode, context: &Context, scope: &mut Sco if let FlowNodeKind::Sleep(duration) = node.kind() { return Ok(Sleep { - duration: duration.clone(), + duration: *duration, next: next_node, }); } diff --git a/src/main.rs b/src/main.rs index bef5c12..1d33ee9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,7 @@ async fn main() -> Result<(), Box> { let store_rx = store.notifier(); let registry_clone = flow_registry.clone(); task::spawn(async move { - scheduler(scheduler_tx_clone.clone(), scheduler_rx, store_rx, registry_clone).await; + scheduler(scheduler_tx_clone, scheduler_rx, store_rx, registry_clone).await; }); info!("✅ Started scheduler"); @@ -64,7 +64,7 @@ async fn main() -> Result<(), Box> { let store_rx = store.notifier(); task::spawn(async move { - store_listener(store_rx, flow_registry, scheduler_tx.clone()).await; + store_listener(store_rx, flow_registry, scheduler_tx).await; }); info!("✅ Initialized store listener"); @@ -82,7 +82,7 @@ async fn main() -> Result<(), Box> { info!("✅ Discovered all devices"); info!("🔥 {} is up and running", env!("CARGO_PKG_NAME")); - hue::observe(tx.clone(), &hue_client, &config).await?; + hue::observe(tx, &hue_client, &config).await?; match signal::ctrl_c().await { Ok(()) => {} From cf6e4eef58cc76db41ad3f7c2a8b4a726b2cf3d7 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Sat, 18 Oct 2025 17:21:04 +0200 Subject: [PATCH 07/10] Enable resuming flows from specific nodes Adds the ability to execute flows starting from a specified node, rather than always from the beginning. This enhances flexibility and allows resuming flows from interruption points. Introduces the `execute_flow` function to facilitate execution from a specific node. Refactors the `execute_flows` function and the flow engine to support this new functionality. Adds logging to aid in debugging and monitoring flow execution. Also moves command dispatching to its own function to remove duplication. --- src/execute_flows.rs | 41 +++++++++++++------- src/flow_engine/engine.rs | 74 +++++++++++++++++++++++++++++------- src/flow_engine/flow.rs | 20 ++++++++-- src/flow_engine/scheduler.rs | 27 +++++++++---- src/flow_loader/factory.rs | 69 ++++++++++++++++++++++++++++----- 5 files changed, 183 insertions(+), 48 deletions(-) diff --git a/src/execute_flows.rs b/src/execute_flows.rs index 3e55d11..2df7fd5 100644 --- a/src/execute_flows.rs +++ b/src/execute_flows.rs @@ -15,27 +15,24 @@ use tracing::{instrument, warn}; type CommandMap = HashMap>; +#[instrument(skip_all, fields(flow = flow.name(), node_id = node_id.as_deref().unwrap_or("")))] +pub async fn execute_flow(flow: Arc, node_id: Option, snapshot: StoreSnapshot, tx: Sender) { + let context = Context::new(snapshot.clone()); + let result = flow_engine::execute(&flow, node_id, &context, tx).await; + + let command_map = merge_command_maps(vec![result]); + dispatch_commands(&snapshot, command_map).await; +} + #[instrument(skip_all)] pub async fn execute_flows(flows: Vec>, snapshot: StoreSnapshot, tx: Sender) { let context = Context::new(snapshot.clone()); - let results = FuturesUnordered::from_iter(flows.iter().map(|flow| async { flow_engine::execute(flow, &context, tx.clone()).await })) + let results = FuturesUnordered::from_iter(flows.iter().map(|flow| async { flow_engine::execute(flow, None, &context, tx.clone()).await })) .collect::>() .await; let command_map = merge_command_maps(results); - for (device_id, properties) in command_map { - if let Some(device) = snapshot.devices.get(&device_id) { - if let Some(controller) = device.controller_id.and_then(|controller_id| controller_registry::get(controller_id)) { - let command = Command::ControlDevice { - device: device.clone(), - property: Arc::new(properties), - }; - controller.execute(command).await; - } else { - warn!(device_id, "⚠️ Device '{}' is not tied to a controller", device.name); - } - } - } + dispatch_commands(&snapshot, command_map).await; } fn merge_command_maps(reports: Vec>) -> CommandMap { @@ -53,6 +50,22 @@ fn merge_command_maps(reports: Vec> merged_map } +async fn dispatch_commands(snapshot: &StoreSnapshot, command_map: CommandMap) { + for (device_id, properties) in command_map { + if let Some(device) = snapshot.devices.get(&device_id) { + if let Some(controller) = device.controller_id.and_then(|controller_id| controller_registry::get(controller_id)) { + let command = Command::ControlDevice { + device: device.clone(), + property: Arc::new(properties), + }; + controller.execute(command).await; + } else { + warn!(device_id, "⚠️ Device '{}' is not tied to a controller", device.name); + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/flow_engine/engine.rs b/src/flow_engine/engine.rs index 2ffb42f..8a3071e 100644 --- a/src/flow_engine/engine.rs +++ b/src/flow_engine/engine.rs @@ -14,7 +14,7 @@ use tokio::time::Instant; use tracing::{debug, info, instrument, trace, warn}; #[instrument(fields(flow = flow.name()), skip_all)] -pub async fn execute(flow: &Flow, context: &Context, tx: Sender) -> Result { +pub async fn execute(flow: &Flow, node_id: Option, context: &Context, tx: Sender) -> Result { debug!("⚖️ Evaluating trigger condition for flow..."); let result = evaluate(flow.trigger(), context); match result { @@ -33,14 +33,23 @@ pub async fn execute(flow: &Flow, context: &Context, tx: Sender Some(node), End => None, Sleep { duration, next } => { tx.send(SchedulerCommand::ScheduleOnce { - flow_name: flow.name().to_string(), + flow_id: flow.id().to_string(), node_id: next.id().to_string(), delay: duration, }) @@ -100,6 +109,8 @@ pub enum FlowEngineError { FailedTriggerEvaluation(ExpressionError), #[error(transparent)] FailedScheduleSleepCommand(#[from] SendError), + #[error("missing provided start node '{0}'")] + MissingProvidedStartNode(String), } pub struct FlowExecutionReport { @@ -155,10 +166,10 @@ mod tests { ); let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(log_node), None)], FlowNodeKind::Start); - let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node).unwrap(); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, Arc::new(start_node), HashMap::new()).unwrap(); let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); - let result = execute(&flow, &Context::default(), scheduler_tx).await; + let result = execute(&flow, None, &Context::default(), scheduler_tx).await; assert!(result.is_ok()); } @@ -170,25 +181,36 @@ mod tests { "flow".to_string(), None, Some(Expression::Literal { value: Value::Boolean(false) }), - start_node, + Arc::new(start_node), + HashMap::new(), ) .unwrap(); let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); - let result = execute(&flow, &Context::default(), scheduler_tx).await; + let result = execute(&flow, None, &Context::default(), scheduler_tx).await; assert!(result.is_ok()); let result = result.unwrap(); assert!(result.scope.is_empty()); assert_eq!(result.duration, Duration::ZERO); } + #[test(tokio::test)] + async fn fails_if_the_start_node_id_cannot_be_found() { + let start_node = FlowNode::new("startNode".to_string(), vec![], FlowNodeKind::Start); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, Arc::new(start_node), HashMap::new()).unwrap(); + + let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); + let result = execute(&flow, Some("unknown".to_string()), &Context::default(), scheduler_tx).await; + assert!(matches!(result, Err(FlowEngineError::MissingProvidedStartNode(_)))); + } + #[test(tokio::test)] async fn fails_if_an_outgoing_node_is_missing() { let start_node = FlowNode::new("startNode".to_string(), vec![], FlowNodeKind::Start); - let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node).unwrap(); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, Arc::new(start_node), HashMap::new()).unwrap(); let (scheduler_tx, _scheduler_rx) = mpsc::channel::(32); - let result = execute(&flow, &Context::default(), scheduler_tx).await; + let result = execute(&flow, None, &Context::default(), scheduler_tx).await; assert!(matches!(result, Err(FlowEngineError::MissingOutgoingNode(_)))); } @@ -203,18 +225,44 @@ mod tests { ); let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(sleep_node), None)], FlowNodeKind::Start); - let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node).unwrap(); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, Arc::new(start_node), HashMap::new()).unwrap(); let (scheduler_tx, mut scheduler_rx) = mpsc::channel::(32); - execute(&flow, &Context::default(), scheduler_tx).await.unwrap(); + execute(&flow, None, &Context::default(), scheduler_tx).await.unwrap(); let received_command = scheduler_rx.recv().await; - if let Some(SchedulerCommand::ScheduleOnce { flow_name, node_id, delay }) = received_command { - assert_eq!(flow_name, "flow"); + if let Some(SchedulerCommand::ScheduleOnce { flow_id, node_id, delay }) = received_command { + assert_eq!(flow_id, "id"); assert_eq!(node_id, "end_node"); assert_eq!(delay, Duration::from_secs(42)); } else { panic!("Expected ScheduleOnce command"); } } + + #[test(tokio::test)] + async fn resumes_execution_at_the_specified_node_id() { + let end_node = Arc::new(FlowNode::new("end_node".to_string(), vec![], FlowNodeKind::End)); + + let sleep_node = Arc::new(FlowNode::new( + "sleep_node".to_string(), + vec![FlowLink::new(end_node.clone(), None)], + FlowNodeKind::Sleep(Duration::from_secs(42)), + )); + + let start_node = Arc::new(FlowNode::new("startNode".to_string(), vec![FlowLink::new(sleep_node.clone(), None)], FlowNodeKind::Start)); + let nodes_by_id = HashMap::from([ + (start_node.id().to_string(), start_node.clone()), + (sleep_node.id().to_string(), sleep_node.clone()), + (end_node.id().to_string(), end_node.clone()), + ]); + let flow = Flow::new("id".to_string(), "flow".to_string(), None, None, start_node, nodes_by_id).unwrap(); + + let (scheduler_tx, mut scheduler_rx) = mpsc::channel::(32); + let result = execute(&flow, Some("end_node".to_string()), &Context::default(), scheduler_tx).await.unwrap(); + + // Ensure that nothing was scheduled + assert!(scheduler_rx.try_recv().is_err(), "Expected no scheduler commands to be sent"); + assert!(result.scope.is_empty()); + } } diff --git a/src/flow_engine/flow.rs b/src/flow_engine/flow.rs index 7f970e8..615bab0 100644 --- a/src/flow_engine/flow.rs +++ b/src/flow_engine/flow.rs @@ -1,21 +1,30 @@ use crate::flow_engine::Expression::Literal; use crate::flow_engine::action::Action; use crate::flow_engine::{Expression, Value}; +use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; use std::time::Duration; -#[derive(Debug)] +#[cfg_attr(not(test), derive(Debug))] pub struct Flow { id: String, name: String, schedule: Option, trigger: Expression, - start_node: FlowNode, + start_node: Arc, + nodes_by_id: HashMap>, } impl Flow { - pub fn new(id: String, name: String, schedule: Option, trigger: Option, start_node: FlowNode) -> Result { + pub fn new( + id: String, + name: String, + schedule: Option, + trigger: Option, + start_node: Arc, + nodes_by_id: HashMap>, + ) -> Result { match start_node.kind { FlowNodeKind::Start => Ok(Flow { id, @@ -23,6 +32,7 @@ impl Flow { schedule, trigger: trigger.unwrap_or(Literal { value: Value::Boolean(true) }), start_node, + nodes_by_id, }), _ => Err("start_node must be of type FlowNodeKind::Start".to_string()), } @@ -47,6 +57,10 @@ impl Flow { pub fn schedule(&self) -> Option<&str> { self.schedule.as_deref() } + + pub fn node_by_id(&self, id: &str) -> Option<&FlowNode> { + self.nodes_by_id.get(id).map(|node| node.as_ref()) + } } #[derive(Debug)] diff --git a/src/flow_engine/scheduler.rs b/src/flow_engine/scheduler.rs index 59207ca..6dd76c8 100644 --- a/src/flow_engine/scheduler.rs +++ b/src/flow_engine/scheduler.rs @@ -1,4 +1,4 @@ -use crate::execute_flows::execute_flows; +use crate::execute_flows::{execute_flow, execute_flows}; use crate::flow_registry::FlowRegistry; use crate::store::StoreSnapshot; use chrono::Utc; @@ -14,7 +14,7 @@ use tracing::{debug, error, info, instrument, warn}; #[derive(Debug)] pub enum SchedulerCommand { Schedule { flow_id: String }, - ScheduleOnce { flow_name: String, node_id: String, delay: Duration }, + ScheduleOnce { flow_id: String, node_id: String, delay: Duration }, } #[instrument(skip_all)] @@ -64,12 +64,23 @@ pub async fn scheduler(tx: Sender, mut rx: Receiver { - info!("🕗 Scheduling flow '{}' to run node '{}' after {:?}... OK", flow, node, delay); + SchedulerCommand::ScheduleOnce { flow_id, node_id, delay } => { + let Some(flow) = flow_registry.by_id(&flow_id) else { + warn!("🕗 Scheduling flow '{}'... failed, flow not found", flow_id); + return; + }; + + debug!("🕗 Scheduling flow '{}' to run node '{}' after {:?}... OK", flow_id, node_id, delay); + let notifier_rx_clone = notifier_rx.clone(); + let tx_clone = tx.clone(); + tokio::spawn(async move { + let scheduled_instant = Instant::now() + Duration::from_millis(delay.as_millis() as u64); + sleep_until(scheduled_instant).await; + + debug!("🕗 Waking up flow '{}'...", flow.name()); + let snapshot = notifier_rx_clone.borrow().clone(); + execute_flow(flow, Some(node_id), snapshot, tx_clone.clone()).await; + }); } } } diff --git a/src/flow_loader/factory.rs b/src/flow_loader/factory.rs index 26278ec..6fbf4a8 100644 --- a/src/flow_loader/factory.rs +++ b/src/flow_loader/factory.rs @@ -25,7 +25,7 @@ pub fn from_json(json: &str) -> Result { nodes_to_visit.reserve(nodes.len()); // Reserve capacity to avoid multiple reallocations let mut flow_node_map: HashMap> = HashMap::new(); - let mut start_node: Option = None; + let mut start_node: Option> = None; while let Some(serialized_node) = nodes_to_visit.pop_back() { let incoming_nodes: Vec = nodes._extract_if(|node| match node { @@ -50,12 +50,14 @@ pub fn from_json(json: &str) -> Result { let outgoing_nodes = map_outgoing_nodes(&serialized_node, &flow_node_map)?; let node = to_flow_node(serialized_node, outgoing_nodes); - if matches!(node.kind(), FlowNodeKind::Start) { - // Take ownership of the node as the start node is the last node to traverse - start_node = Some(node); - } else { - flow_node_map.insert(node.id().to_owned(), Arc::new(node)); + let node_id = node.id().to_owned(); + let node_arc = Arc::new(node); + + if matches!(node_arc.kind(), FlowNodeKind::Start) { + start_node = Some(node_arc.clone()); } + + flow_node_map.insert(node_id, node_arc); } if !nodes.is_empty() { @@ -70,6 +72,7 @@ pub fn from_json(json: &str) -> Result { flow.schedule, flow.trigger, start_node.ok_or_else(|| FlowFactoryError::MissingStartNode)?, + flow_node_map, ) .expect("Flow creation failed"); Ok(flow) @@ -199,7 +202,15 @@ mod tests { let end_node = FlowNode::new("endNode".to_string(), vec![], FlowNodeKind::End); let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(end_node), None)], FlowNodeKind::Start); - let expected = Flow::new("01K7KK65D87SZGGZE7VB8QYT20".to_string(), "emptyFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new( + "01K7KK65D87SZGGZE7VB8QYT20".to_string(), + "emptyFlow".to_string(), + None, + None, + Arc::new(start_node), + HashMap::new(), + ) + .unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } @@ -218,7 +229,15 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(action_node), None)], FlowNodeKind::Start); - let expected = Flow::new("01K7KK6H5R7Y72QJEJSJQCKMRQ".to_string(), "logFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new( + "01K7KK6H5R7Y72QJEJSJQCKMRQ".to_string(), + "logFlow".to_string(), + None, + None, + Arc::new(start_node), + HashMap::new(), + ) + .unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } @@ -240,7 +259,15 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(action_node), None)], FlowNodeKind::Start); - let expected = Flow::new("01K7KK5FC54SN8D4QYVNEGFYG4".to_string(), "controlDeviceFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new( + "01K7KK5FC54SN8D4QYVNEGFYG4".to_string(), + "controlDeviceFlow".to_string(), + None, + None, + Arc::new(start_node), + HashMap::new(), + ) + .unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } @@ -259,7 +286,29 @@ mod tests { let start_node = FlowNode::new("startNode".to_string(), vec![FlowLink::new(Arc::new(sleep_node), None)], FlowNodeKind::Start); - let expected = Flow::new("01K7KK7E6GG26XZZDXSGFZCWQ4".to_string(), "sleepFlow".to_string(), None, None, start_node).unwrap(); + let expected = Flow::new( + "01K7KK7E6GG26XZZDXSGFZCWQ4".to_string(), + "sleepFlow".to_string(), + None, + None, + Arc::new(start_node), + HashMap::new(), + ) + .unwrap(); assert_eq!(format!("{:#?}", flow), format!("{:#?}", expected)); } } + +#[cfg(test)] +impl std::fmt::Debug for Flow { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Ignores the nodes_by_id field as the order is not deterministic and all nodes are reachable by start node + f.debug_struct("Flow") + .field("id", &self.id()) + .field("name", &self.name()) + .field("schedule", &self.schedule()) + .field("trigger", &self.trigger()) + .field("start_node", &self.start_node()) + .finish() + } +} From 0e8efe0a76ccbe94080e583d80e33ebf099e489b Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Sat, 18 Oct 2025 19:06:51 +0200 Subject: [PATCH 08/10] Remove unnecessary parentheses --- src/flow_engine/expression.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flow_engine/expression.rs b/src/flow_engine/expression.rs index 1cc3508..2ea5b76 100644 --- a/src/flow_engine/expression.rs +++ b/src/flow_engine/expression.rs @@ -92,7 +92,7 @@ pub fn evaluate(expression: &Expression, context: &Context) -> Result match (evaluate(expression, context)?) { + Not { expression } => match evaluate(expression, context)? { Value::Boolean(b) => Ok(Value::Boolean(!b)), _ => Err(ExpressionError::UnaryOperandTypeMismatch { operand: "Not", From f7b783f08ca9e08a64a88cb0a9cdbe46acc1fad7 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Sat, 18 Oct 2025 19:08:24 +0200 Subject: [PATCH 09/10] Add MacOS .DS_Store file to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2087673..cb42a03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .idea/ /target config_local.json5 + +# MacOS +.DS_Store From 52e8f7e89609a7b29e4203823e3c19e51f09c060 Mon Sep 17 00:00:00 2001 From: Johan Flint Date: Sat, 18 Oct 2025 19:20:44 +0200 Subject: [PATCH 10/10] Update dependencies Updates dependencies to their latest versions. Removes some unnecessary dependencies, improving the project's dependency footprint. --- Cargo.lock | 841 ++++++++++++++++++--------------------- Cargo.toml | 20 +- action_macros/Cargo.lock | 16 +- action_macros/Cargo.toml | 8 +- 4 files changed, 399 insertions(+), 486 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7ec097..4de90fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,27 +6,12 @@ version = 4 name = "action_macros" version = "0.1.0" dependencies = [ - "convert_case 0.7.1", + "convert_case 0.8.0", "proc-macro2", "quote", "syn", ] -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "aho-corasick" version = "1.1.3" @@ -47,9 +32,9 @@ dependencies = [ [[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", @@ -62,9 +47,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" @@ -77,29 +62,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 = "arraydeque" @@ -119,9 +104,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", @@ -140,21 +125,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base64" version = "0.21.7" @@ -169,9 +139,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "serde", ] @@ -192,7 +162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", - "regex-automata 0.4.9", + "regex-automata", "serde", ] @@ -210,27 +180,27 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" -version = "1.1.10" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" [[package]] name = "cargo-config2" -version = "0.1.35" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f307d010782c2a4066cc5125ba8c6b68db926b3a1bb82bd6d0b38950c6d4815" +checksum = "3795d3a48839a46854805f56c8fe9c558f10804bcf57df53925ca843d87c788f" dependencies = [ "serde", "serde_derive", "toml", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "cargo-llvm-cov" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecc469e8c1be87be84e3e3c32bcc92eedca5bb57d1e7f606e10154f564a1f8d" +checksum = "42236fed339535379c8671218bd8050ddf2b937cb16f6012b44b6cf993ce7c9c" dependencies = [ "anyhow", "camino", @@ -255,18 +225,19 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" @@ -298,9 +269,9 @@ dependencies = [ [[package]] name = "config" -version = "0.15.13" +version = "0.15.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1eb4fb07bc7f012422df02766c7bd5971effb894f573865642f06fa3265440" +checksum = "180e549344080374f9b32ed41bf3b6b57885ff6a289367b3dbc10eea8acc1918" dependencies = [ "async-trait", "convert_case 0.6.0", @@ -308,10 +279,11 @@ dependencies = [ "pathdiff", "ron", "rust-ini", - "serde", + "serde-untagged", + "serde_core", "serde_json", "toml", - "winnow 0.7.12", + "winnow 0.7.13", "yaml-rust2", ] @@ -346,9 +318,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -407,9 +379,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4735f265ba6a1188052ca32d461028a7d1125868be18e287e756019da7607b5" +checksum = "59c9b8bdf64ee849747c1b12eb861d21aa47fa161564f48332f1afe2373bf899" dependencies = [ "ctor-proc-macro", "dtor", @@ -417,9 +389,9 @@ dependencies = [ [[package]] name = "ctor-proc-macro" -version = "0.0.5" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" [[package]] name = "diff" @@ -459,24 +431,24 @@ dependencies = [ [[package]] name = "dtor" -version = "0.0.6" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cbdf2ad6846025e8e25df05171abfb30e3ababa12ee0a0e44b9bbe570633a8" +checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934" dependencies = [ "dtor-proc-macro", ] [[package]] name = "dtor-proc-macro" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" [[package]] name = "duct" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ce170a0e8454fa0f9b0e5ca38a6ba17ed76a50916839d217eb5357e05cdfde" +checksum = "d7478638a31d1f1f3d6c9f5e57c76b906a04ac4879d6fd0fb6245bc88f73fd0b" dependencies = [ "libc", "os_pipe", @@ -495,9 +467,9 @@ dependencies = [ [[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", ] @@ -520,14 +492,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -538,16 +521,22 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "fnv" version = "1.0.7" @@ -577,18 +566,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[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", ] [[package]] name = "fs-err" -version = "3.1.1" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7be93788013f265201256d58f04936a8079ad5dc898743aa20525f503b683" +checksum = "6ad492b2cf1d89d568a43508ab24f98501fe03f2f31c01e1d0fe7366a71745d2" dependencies = [ "autocfg", ] @@ -690,9 +679,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", @@ -706,38 +695,32 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+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 = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -760,20 +743,26 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -868,13 +857,14 @@ dependencies = [ [[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", @@ -882,6 +872,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -921,9 +912,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64 0.22.1", "bytes", @@ -937,7 +928,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2", "system-configuration", "tokio", "tower-service", @@ -1057,9 +1048,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", @@ -1078,23 +1069,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.15.4", -] - -[[package]] -name = "io-uring" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" -dependencies = [ - "bitflags", - "cfg-if", - "libc", + "hashbrown 0.16.0", ] [[package]] @@ -1127,9 +1107,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -1172,15 +1152,15 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libredox" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ "bitflags", "libc", @@ -1189,9 +1169,9 @@ dependencies = [ [[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" @@ -1201,34 +1181,33 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -1236,24 +1215,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -1299,21 +1269,20 @@ dependencies = [ [[package]] name = "normpath" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -1325,15 +1294,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -1348,20 +1308,20 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "opener" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223" +checksum = "cb9024962ab91e00c89d2a14352a8d0fc1a64346bf96f1839b45c09149564e47" dependencies = [ "bstr", "normpath", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" dependencies = [ "bitflags", "cfg-if", @@ -1391,9 +1351,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" dependencies = [ "cc", "libc", @@ -1413,25 +1373,19 @@ dependencies = [ [[package]] name = "os_pipe" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1439,15 +1393,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -1458,26 +1412,25 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[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 = "pest" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", - "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" dependencies = [ "pest", "pest_generator", @@ -1485,9 +1438,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" dependencies = [ "pest", "pest_meta", @@ -1498,9 +1451,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" dependencies = [ "pest", "sha2", @@ -1546,9 +1499,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -1574,18 +1527,18 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -1601,9 +1554,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -1670,61 +1623,46 @@ 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 = "redox_syscall" -version = "0.5.15" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[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", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -1734,9 +1672,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", @@ -1803,21 +1741,20 @@ dependencies = [ [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", @@ -1833,9 +1770,9 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.21.2" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7295b7ce3bf4806b419dc3420745998b447178b7005e2011947b38fc5aa6791" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" dependencies = [ "cfg-if", "ordered-multimap", @@ -1843,9 +1780,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc_version" @@ -1858,22 +1795,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.29" +version = "0.23.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" dependencies = [ "once_cell", "rustls-pki-types", @@ -1893,9 +1830,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "ring", "rustls-pki-types", @@ -1904,9 +1841,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ruzstd" @@ -1931,11 +1868,11 @@ dependencies = [ [[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]] @@ -1959,9 +1896,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", @@ -1969,24 +1906,46 @@ 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 = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +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", @@ -1995,23 +1954,24 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -2053,15 +2013,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" dependencies = [ "libc", - "sigchld", "windows-sys 0.60.2", ] [[package]] name = "shared_thread" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7a6f98357c6bb0ebace19b22220e5543801d9de90ffe77f8abb27c056bac064" +checksum = "52b86057fcb5423f5018e331ac04623e32d6b5ce85e33300f92c79a1973928b0" [[package]] name = "shell-escape" @@ -2075,32 +2034,11 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "sigchld" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" -dependencies = [ - "libc", - "os_pipe", - "signal-hook", -] - -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -2113,9 +2051,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -2125,29 +2063,19 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -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]] 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" @@ -2157,9 +2085,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", @@ -2220,15 +2148,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]] @@ -2264,18 +2192,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -2312,29 +2240,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.46.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.5.10", + "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -2364,9 +2289,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", @@ -2385,9 +2310,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -2398,50 +2323,45 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "serde", + "serde_core", "serde_spanned", - "toml_datetime 0.7.0", + "toml_datetime", "toml_parser", - "winnow 0.7.12", + "winnow 0.7.13", ] [[package]] name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_datetime" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", - "toml_datetime 0.6.11", - "winnow 0.7.12", + "toml_datetime", + "toml_parser", + "winnow 0.7.13", ] [[package]] name = "toml_parser" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "winnow 0.7.12", + "winnow 0.7.13", ] [[package]] @@ -2534,14 +2454,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -2556,11 +2476,17 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[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 = "ucd-trie" @@ -2570,9 +2496,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-segmentation" @@ -2588,13 +2514,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]] @@ -2653,31 +2580,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -2689,9 +2617,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -2702,9 +2630,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2712,9 +2640,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -2725,9 +2653,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] @@ -2747,45 +2675,23 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.62.2" @@ -2904,7 +2810,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -2925,18 +2840,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "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]] @@ -2947,9 +2863,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -2959,9 +2875,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -2971,9 +2887,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -2983,9 +2899,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -2995,9 +2911,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3007,9 +2923,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3019,9 +2935,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3031,9 +2947,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -3046,21 +2962,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" @@ -3070,9 +2983,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xattr" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", "rustix", @@ -3080,9 +2993,9 @@ dependencies = [ [[package]] name = "yaml-rust2" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" dependencies = [ "arraydeque", "encoding_rs", @@ -3121,18 +3034,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -3162,9 +3075,9 @@ 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" @@ -3179,9 +3092,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", diff --git a/Cargo.toml b/Cargo.toml index 03e142b..4957635 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,24 +9,24 @@ tokio = { version = "1", features = ["full"] } tokio-retry = "0.3.0" tokio-stream = { version = "0.1.17", features = ["fs"] } futures = "0.3" # Needed by reqwest's stream feature -tracing = "0.1.41" -tracing-subscriber = "0.3.19" +tracing = "0.1" +tracing-subscriber = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -config = "0.15.9" +config = "0.15" thiserror = "2.0" async-trait = "0.1" cron = "0.15.0" -chrono = "0.4.42" +chrono = "0.4" humantime-serde = "1.1.1" # Action macros action_macros = { path = "action_macros" } -ctor = "0.4" +ctor = "0.6" [dev-dependencies] -cargo-llvm-cov = "0.6.16" -mockito = "1.7.0" -pretty_assertions = "1.4.1" -rstest = "0.25.0" -test-log = { version = "0.2.17", features = ["trace"] } +cargo-llvm-cov = "0.6" +mockito = "1.7" +pretty_assertions = "1.4" +rstest = "0.26" +test-log = { version = "0.2", features = ["trace"] } diff --git a/action_macros/Cargo.lock b/action_macros/Cargo.lock index eee42b3..5487614 100644 --- a/action_macros/Cargo.lock +++ b/action_macros/Cargo.lock @@ -14,36 +14,36 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "syn" -version = "2.0.98" +version = "2.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", diff --git a/action_macros/Cargo.toml b/action_macros/Cargo.toml index b6ff387..05c2f64 100644 --- a/action_macros/Cargo.toml +++ b/action_macros/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" proc-macro = true [dependencies] -quote = "1.0.38" -syn = "2.0.98" -proc-macro2 = "1.0.93" -convert_case = "0.7.1" +quote = "1.0.41" +syn = "2.0.107" +proc-macro2 = "1.0.101" +convert_case = "0.8.0"