1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! # Cano: Type-Safe Async Workflow Engine
//!
//! Cano is an async workflow orchestration engine for Rust built on Finite State Machines (FSM).
//! States are user-defined enums; the engine guarantees type-safe transitions between them.
//!
//! Well-suited for:
//! - Data pipelines: ETL jobs with parallel processing (Split/Join) and aggregation
//! - AI agents: Multi-step inference chains with shared context
//! - Background systems: Scheduled maintenance, periodic reporting, cron jobs
//!
//! ## Quick Start
//!
//! ```rust
//! use cano::prelude::*;
//!
//! #[derive(Debug, Clone, PartialEq, Eq, Hash)]
//! enum Step { Fetch, Process, Done }
//!
//! struct FetchTask;
//! struct ProcessTask;
//!
//! #[async_trait]
//! impl Task<Step> for FetchTask {
//! async fn run(&self, store: &MemoryStore) -> Result<TaskResult<Step>, CanoError> {
//! store.put("data", vec![1u32, 2, 3])?;
//! Ok(TaskResult::Single(Step::Process))
//! }
//! }
//!
//! #[async_trait]
//! impl Task<Step> for ProcessTask {
//! async fn run(&self, store: &MemoryStore) -> Result<TaskResult<Step>, CanoError> {
//! let data: Vec<u32> = store.get("data")?;
//! store.put("sum", data.iter().sum::<u32>())?;
//! Ok(TaskResult::Single(Step::Done))
//! }
//! }
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), CanoError> {
//! let store = MemoryStore::new();
//! let workflow = Workflow::new(store.clone())
//! .register(Step::Fetch, FetchTask)
//! .register(Step::Process, ProcessTask)
//! .add_exit_state(Step::Done);
//!
//! let final_state = workflow.orchestrate(Step::Fetch).await?;
//! assert_eq!(final_state, Step::Done);
//! # Ok(())
//! # }
//! ```
//!
//! ## Core Concepts
//!
//! ### Finite State Machines (FSM)
//!
//! Workflows in Cano are state machines. You define your states as an `enum`, register
//! handlers ([`Task`] or [`Node`]) for each state, and the engine manages transitions.
//!
//! ### Tasks and Nodes
//!
//! Two interfaces for processing logic:
//! - [`Task`] trait: single `run()` method — straightforward operations and prototyping
//! - [`Node`] trait: three-phase lifecycle (`prep` → `exec` → `post`) with built-in retry
//!
//! Every [`Node`] automatically implements [`Task`] via a blanket impl, so both can be
//! registered with the same [`Workflow::register`] method.
//!
//! ### Parallel Execution (Split/Join)
//!
//! Run tasks concurrently with [`Workflow::register_split`] and join results using
//! strategies: `All`, `Any`, `Quorum(n)`, `Percentage(f64)`, `PartialResults(min)`,
//! or `PartialTimeout`.
//!
//! ### Store
//!
//! [`MemoryStore`] provides a thread-safe `Arc<RwLock<HashMap>>` for sharing typed data
//! between states. Implement [`KeyValueStore`] to plug in a custom backend.
//!
//! ## Processing Lifecycle
//!
//! **Task**: Single `run()` method — full control over execution flow.
//!
//! **Node**: Three-phase lifecycle (retried as a unit on `prep` or `post` failure):
//! 1. `prep` — load data, validate inputs, allocate resources
//! 2. `exec` — core logic (infallible — signature returns result directly)
//! 3. `post` — write results, determine next state
//!
//! ## Module Overview
//!
//! - [`task`]: The [`Task`] trait — single `run()` method
//! - [`node`]: The [`Node`] trait — three-phase lifecycle with retry via [`TaskConfig`]
//! - [`workflow`]: [`Workflow`] — FSM orchestration with Split/Join support
//! - [`scheduler`] (requires `scheduler` feature): [`Scheduler`] — cron and interval scheduling
//! - [`store`]: [`MemoryStore`] and the [`KeyValueStore`] trait
//! - [`error`]: [`CanoError`] variants and the [`CanoResult`] alias
//!
//! ## Getting Started
//!
//! 1. Run an example: `cargo run --example workflow_simple`
//! 2. Read the module docs — each module has detailed documentation and examples
//! 3. Run benchmarks: `cargo bench --bench node_performance`
// Core public API - simplified imports
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
// Convenience re-exports for common patterns