Skip to main content

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}