Skip to main content

swiftide_tasks/
lib.rs

1//! Build and run typed task graphs.
2//!
3//! Tasks connect [`TaskNode`] implementations into a small execution graph. Each node receives a
4//! typed input, produces a typed output, and hands control to the next step through a
5//! [`Transition`].
6//!
7//! The main entry points are:
8//! - [`Task`] to define and run a graph
9//! - [`Transition`] to describe how execution should continue after a node finishes
10//! - [`Task::register_node_fn`] for lightweight synchronous closure nodes
11//! - [`NodeId`] helpers such as [`NodeId::transitions_with`] and [`NodeId::join`] for the common
12//!   next-step and join cases
13//! - [`Transition::fan_out`] for typed static fan-out branches
14//!
15//! # Examples
16//!
17//! A small linear task:
18//!
19//! ```no_run
20//! use swiftide_tasks::{NodeError, Task, TaskRunOutcome};
21//!
22//! # #[tokio::main(flavor = "current_thread")]
23//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
24//! let mut task = Task::<i32, i32>::new();
25//!
26//! let start = task.register_node_fn(|input: &i32| -> Result<i32, NodeError> { Ok(*input + 1) });
27//! let finish =
28//!     task.register_node_fn(|input: &i32| -> Result<i32, NodeError> { Ok(*input * 2) });
29//!
30//! task.starts_with(start);
31//! task.register_transition(start, move |value| finish.transitions_with(value))?;
32//! task.register_transition(finish, task.transitions_to_finish())?;
33//!
34//! let result = task.run(2).await?;
35//! assert_eq!(result, TaskRunOutcome::Completed(6));
36//! # Ok(())
37//! # }
38//! ```
39//!
40//! A fan-out with an explicit join:
41//!
42//! ```no_run
43//! use swiftide_tasks::{JoinInput, NodeError, Task, TaskRunOutcome, Transition};
44//!
45//! # #[tokio::main(flavor = "current_thread")]
46//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
47//! let mut task = Task::<i32, i32>::new();
48//!
49//! let start = task.register_node_fn(|input: &i32| -> Result<i32, NodeError> { Ok(*input) });
50//! let double =
51//!     task.register_node_fn(|input: &i32| -> Result<i32, NodeError> { Ok(*input * 2) });
52//! let increment =
53//!     task.register_node_fn(|input: &i32| -> Result<i32, NodeError> { Ok(*input + 1) });
54//! let join = task.register_node_fn(|input: &JoinInput<i32>| -> Result<i32, NodeError> {
55//!     Ok(input.iter().copied().sum())
56//! });
57//!
58//! task.starts_with(start);
59//! task.register_transition(start, move |value| {
60//!     // `join_with` defines the branch group and the join node that waits for it.
61//!     Transition::fan_out(&double, value)
62//!         .and(&increment, value)
63//!         .join_with(join.join())
64//! })?;
65//! // Branches still decide where their own output goes before the join can run. Fan-out branches
66//! // cannot finish independently; use a join node as the single place that finishes the task.
67//! task.register_transition(double, join.join())?;
68//! task.register_transition(increment, join.join())?;
69//! task.register_transition(join, task.transitions_to_finish())?;
70//!
71//! let result = task.run(3).await?;
72//! assert_eq!(result, TaskRunOutcome::Completed(10));
73//! # Ok(())
74//! # }
75//! ```
76mod adapters;
77mod errors;
78mod executor;
79mod node;
80mod runtime;
81mod task;
82mod traits;
83mod transition;
84
85pub use adapters::{AsyncFn, SyncFn};
86pub use errors::{NodeError, TaskError};
87pub use node::NodeId;
88pub use task::{CurrentNodes, Task, TaskRunOutcome};
89pub use traits::{DynNodeId, NodeArg, TaskNode};
90pub use transition::{
91    AnyJoinInput, AnyJoinTarget, ConcurrencyModel, FanOutTransition, JoinInput, JoinTarget,
92    MappedJoinTarget, MarkedTransition, Transition,
93};