rust_supervisor/task/factory.rs
1//! Task factory and service adapter types.
2//!
3//! This module owns the public task construction contract. Every call to
4//! [`TaskFactory::build`] must create a fresh future for one attempt.
5
6use crate::error::types::TaskFailure;
7use crate::task::context::TaskContext;
8use std::future::Future;
9use std::pin::Pin;
10
11/// Boxed task future returned by task factories.
12pub type BoxTaskFuture = Pin<Box<dyn Future<Output = TaskResult> + Send + 'static>>;
13
14/// Result produced by a supervised task attempt.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum TaskResult {
17 /// The task completed successfully.
18 Succeeded,
19 /// The task observed cancellation and stopped cooperatively.
20 Cancelled,
21 /// The task failed with a typed failure payload.
22 Failed(TaskFailure),
23}
24
25impl TaskResult {
26 /// Returns whether this task result is successful.
27 ///
28 /// # Arguments
29 ///
30 /// This function has no arguments.
31 ///
32 /// # Returns
33 ///
34 /// Returns `true` only for [`TaskResult::Succeeded`].
35 ///
36 /// # Examples
37 ///
38 /// ```
39 /// let result = rust_supervisor::task::factory::TaskResult::Succeeded;
40 /// assert!(result.is_success());
41 /// ```
42 pub fn is_success(&self) -> bool {
43 matches!(self, Self::Succeeded)
44 }
45}
46
47/// Factory that creates a fresh task future for each attempt.
48pub trait TaskFactory: Send + Sync + 'static {
49 /// Builds a new task future.
50 ///
51 /// # Arguments
52 ///
53 /// - `ctx`: Per-attempt context with cancellation, heartbeat, and readiness.
54 ///
55 /// # Returns
56 ///
57 /// Returns a boxed future that resolves to [`TaskResult`].
58 fn build(&self, ctx: TaskContext) -> BoxTaskFuture;
59}
60
61/// Service adapter that can be converted into a [`TaskFactory`].
62pub trait Service: Send + Sync + 'static {
63 /// Calls the service for one task attempt.
64 ///
65 /// # Arguments
66 ///
67 /// - `ctx`: Per-attempt context passed to the service.
68 ///
69 /// # Returns
70 ///
71 /// Returns a boxed future for the attempt.
72 fn call(&self, ctx: TaskContext) -> BoxTaskFuture;
73}
74
75impl<T> TaskFactory for T
76where
77 T: Service,
78{
79 /// Builds a task future through the service implementation.
80 fn build(&self, ctx: TaskContext) -> BoxTaskFuture {
81 self.call(ctx)
82 }
83}
84
85/// Concrete service adapter returned by [`service_fn`].
86pub struct ServiceFn<F> {
87 /// Closure used to create a fresh task future.
88 function: F,
89}
90
91impl<F, Fut> Service for ServiceFn<F>
92where
93 F: Fn(TaskContext) -> Fut + Send + Sync + 'static,
94 Fut: Future<Output = TaskResult> + Send + 'static,
95{
96 /// Calls the stored function and boxes its returned future.
97 fn call(&self, ctx: TaskContext) -> BoxTaskFuture {
98 Box::pin((self.function)(ctx))
99 }
100}
101
102/// Creates a service from a function or closure.
103///
104/// # Arguments
105///
106/// - `function`: Function that creates a fresh future for each task attempt.
107///
108/// # Returns
109///
110/// Returns a [`Service`] implementation that also implements [`TaskFactory`].
111///
112/// # Examples
113///
114/// ```
115/// let service = rust_supervisor::task::factory::service_fn(|_ctx| async {
116/// rust_supervisor::task::factory::TaskResult::Succeeded
117/// });
118/// let (ctx, _heartbeat) = rust_supervisor::task::context::TaskContext::new(
119/// rust_supervisor::id::types::ChildId::new("worker"),
120/// rust_supervisor::id::types::SupervisorPath::root().join("worker"),
121/// rust_supervisor::id::types::Generation::initial(),
122/// rust_supervisor::id::types::Attempt::first(),
123/// );
124/// let _future = rust_supervisor::task::factory::TaskFactory::build(&service, ctx);
125/// ```
126pub fn service_fn<F, Fut>(function: F) -> ServiceFn<F>
127where
128 F: Fn(TaskContext) -> Fut + Send + Sync + 'static,
129 Fut: Future<Output = TaskResult> + Send + 'static,
130{
131 ServiceFn { function }
132}