foxtive_supervisor/lib.rs
1//! # 🦊 Foxtive Supervisor
2//!
3//! A Rust supervision library that keeps your async tasks running, even when things go wrong.
4//! It provides automatic restarts, dependency management, dynamic task management, and deep observability.
5//!
6//! ## Core Components
7//!
8//! - [`Supervisor`]: The primary entry point for configuring and starting supervised tasks.
9//! - [`TaskRuntime`]: The underlying runtime handle for managing tasks after they've started.
10//! - [`SupervisedTask`]: The trait your tasks must implement to be supervised.
11//! - [`persistence::TaskStateStore`]: Interface for persisting task state across restarts.
12//! - [`SupervisorEventListener`]: Interface for listening to supervisor-wide events.
13
14pub mod contracts;
15pub mod enums;
16pub mod error;
17pub mod runtime;
18pub mod persistence;
19pub mod hierarchy;
20pub mod task_pool;
21
22#[cfg(feature = "distributed")]
23pub mod distributed;
24
25use std::future::Future;
26use std::sync::Arc;
27
28pub use crate::contracts::{SupervisedTask, SupervisorEventListener};
29pub use crate::enums::TaskConfig;
30pub use crate::error::{SupervisorError, ValidationError};
31pub use crate::persistence::TaskStateStore;
32pub use crate::runtime::{SupervisionResult, TaskRuntime, spawn_supervised, spawn_supervised_many};
33
34/// Builder for constructing and starting a supervisor.
35///
36/// `Supervisor` provides a fluent API for registering tasks, setting up prerequisites,
37/// and configuring global runtime options like concurrency limits and persistence.
38///
39/// # Example
40///
41/// ```rust
42/// use foxtive_supervisor::{Supervisor, SupervisedTask};
43///
44/// struct MyTask;
45/// #[async_trait::async_trait]
46/// impl SupervisedTask for MyTask {
47/// fn id(&self) -> &'static str { "my-task" }
48/// async fn run(&self) -> anyhow::Result<()> { Ok(()) }
49/// }
50///
51/// #[tokio::main]
52/// async fn main() {
53/// Supervisor::new()
54/// .add(MyTask)
55/// .start_and_wait_any()
56/// .await
57/// .unwrap();
58/// }
59/// ```
60pub struct Supervisor {
61 runtime: crate::runtime::TaskRuntime,
62}
63
64impl Supervisor {
65 /// Create a new, empty supervisor.
66 pub fn new() -> Self {
67 Self {
68 runtime: crate::runtime::TaskRuntime::new(),
69 }
70 }
71
72 /// Add a task to be supervised.
73 ///
74 /// The task must implement the [`SupervisedTask`] trait.
75 #[allow(clippy::should_implement_trait)]
76 pub fn add<T: SupervisedTask + 'static>(mut self, task: T) -> Self {
77 self.runtime.register(task);
78 self
79 }
80
81 /// Add multiple tasks of the same type.
82 pub fn add_many<T: SupervisedTask + 'static>(mut self, tasks: Vec<T>) -> Self {
83 self.runtime.register_many(tasks);
84 self
85 }
86
87 /// Add a task already wrapped in a `Box`.
88 ///
89 /// Useful when managing a heterogeneous collection of tasks.
90 pub fn add_boxed(mut self, task: Box<dyn SupervisedTask>) -> Self {
91 self.runtime.register_boxed(task);
92 self
93 }
94
95 /// Add a task already wrapped in an `Arc`.
96 ///
97 /// This is the most efficient way to add a task if you already have an `Arc` handle.
98 pub fn add_arc(mut self, task: Arc<dyn SupervisedTask>) -> Self {
99 self.runtime.register_arc(task);
100 self
101 }
102
103 /// Set a global concurrency limit for the supervisor.
104 ///
105 /// This limits how many tasks can be in their `run()` loop simultaneously.
106 /// It's useful for preventing resource exhaustion during massive task restarts.
107 pub fn with_global_concurrency_limit(mut self, limit: usize) -> Self {
108 self.runtime.with_global_concurrency_limit(limit);
109 self
110 }
111
112 /// Register an event listener to observe lifecycle events.
113 ///
114 /// Event listeners receive notifications for task starts, failures, restarts, etc.
115 pub fn add_listener(mut self, listener: Arc<dyn SupervisorEventListener>) -> Self {
116 self.runtime.add_listener(listener);
117 self
118 }
119
120 /// Set a custom state store for persisting task states across restarts.
121 ///
122 /// The supervisor will use this store to load previous attempt counts and failure metadata
123 /// before starting tasks, and will update it as tasks run.
124 pub fn with_state_store(mut self, store: Arc<dyn TaskStateStore>) -> Self {
125 self.runtime.with_state_store(store);
126 self
127 }
128
129 /// Require a named async gate to resolve before any supervised task starts.
130 ///
131 /// Prerequisites run sequentially in the order they were registered.
132 /// If any prerequisite fails, the supervisor aborts startup.
133 pub fn require<F>(mut self, name: &'static str, fut: F) -> Self
134 where
135 F: Future<Output = anyhow::Result<()>> + Send + 'static,
136 {
137 self.runtime.add_prerequisite(name, Box::pin(fut));
138 self
139 }
140
141 /// Require a named async gate using a closure.
142 pub fn require_fn<F, Fut>(mut self, name: &'static str, f: F) -> Self
143 where
144 F: FnOnce() -> Fut + Send + 'static,
145 Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
146 {
147 self.runtime.add_prerequisite_fn(name, f);
148 self
149 }
150
151 /// Consume the builder and return the underlying [`TaskRuntime`].
152 ///
153 /// Note: This does not start the tasks. You must call `start_all()` on the returned runtime.
154 pub fn runtime(self) -> crate::runtime::TaskRuntime {
155 self.runtime
156 }
157
158 /// Start all registered tasks and return the [`TaskRuntime`] handle.
159 ///
160 /// This validates the dependency graph and executes prerequisites before spawning tasks.
161 ///
162 /// # Errors
163 /// Returns [`SupervisorError`] if prerequisites fail or if the dependency graph is invalid.
164 pub async fn start(
165 mut self,
166 ) -> Result<crate::runtime::TaskRuntime, crate::error::SupervisorError> {
167 self.runtime.start_all().await?;
168 Ok(self.runtime)
169 }
170
171 /// Start all tasks and block until the first one terminates.
172 ///
173 /// # Errors
174 /// Returns [`SupervisorError`] if startup validation or prerequisites fail.
175 pub async fn start_and_wait_any(
176 mut self,
177 ) -> Result<SupervisionResult, crate::error::SupervisorError> {
178 self.runtime.start_all().await?;
179 Ok(self.runtime.wait_any().await)
180 }
181
182 /// Start all tasks and block until all have terminated.
183 ///
184 /// # Errors
185 /// Returns [`SupervisorError`] if startup validation or prerequisites fail.
186 pub async fn start_and_wait_all(
187 mut self,
188 ) -> Result<Vec<SupervisionResult>, crate::error::SupervisorError> {
189 self.runtime.start_all().await?;
190 Ok(self.runtime.wait_all().await)
191 }
192}
193
194impl Default for Supervisor {
195 /// Create a new supervisor with default settings.
196 fn default() -> Self {
197 Self::new()
198 }
199}