Skip to main content

rust_supervisor/role/adapter/
job.rs

1//! Job role adapter.
2//!
3//! The adapter owns the bridge from [`JobRole`] lifecycle methods to the
4//! existing [`TaskFactory`](crate::task::factory::TaskFactory) runtime contract.
5
6use crate::role::context::job::JobContext;
7use crate::role::result::job::JobError;
8use crate::role::traits::job::JobRole;
9use crate::task::context::TaskContext;
10use crate::task::factory::{BoxTaskFuture, Service, TaskResult};
11use std::sync::Arc;
12use tokio::sync::Mutex;
13
14/// Adapter that turns a job role into a task factory service.
15pub struct JobRoleAdapter<T> {
16    /// Role instance protected across runtime task starts.
17    role: Arc<Mutex<T>>,
18}
19
20impl<T> JobRoleAdapter<T>
21where
22    T: JobRole,
23{
24    /// Creates a job role adapter.
25    ///
26    /// # Arguments
27    ///
28    /// - `role`: Job role instance that owns user lifecycle state.
29    ///
30    /// # Returns
31    ///
32    /// Returns an adapter that implements the task factory service contract.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use rust_supervisor::role::adapter::job::JobRoleAdapter;
38    /// use rust_supervisor::role::context::job::JobContext;
39    /// use rust_supervisor::role::result::job::JobResult;
40    /// use rust_supervisor::role::traits::job::JobRole;
41    ///
42    /// struct ExampleJob;
43    ///
44    /// impl JobRole for ExampleJob {
45    ///     async fn run(&mut self, ctx: &JobContext) -> JobResult<()> {
46    ///         let _ = ctx;
47    ///         Ok(())
48    ///     }
49    /// }
50    ///
51    /// let _adapter = JobRoleAdapter::new(ExampleJob);
52    /// ```
53    pub fn new(role: T) -> Self {
54        Self {
55            role: Arc::new(Mutex::new(role)),
56        }
57    }
58}
59
60impl<T> Service for JobRoleAdapter<T>
61where
62    T: JobRole,
63{
64    /// Builds one task future for the adapted job role.
65    ///
66    /// # Arguments
67    ///
68    /// - `ctx`: Runtime task context passed to the job adapter.
69    ///
70    /// # Returns
71    ///
72    /// Returns a boxed future for the child_start_count.
73    fn call(&self, ctx: TaskContext) -> BoxTaskFuture {
74        let role = self.role.clone();
75        let job_context = JobContext::new(ctx);
76        Box::pin(async move {
77            let mut role = role.lock().await;
78            run_job_lifecycle(&mut *role, &job_context).await
79        })
80    }
81}
82
83/// Runs the job lifecycle and maps role errors into task results.
84///
85/// # Arguments
86///
87/// - `role`: Job role implementation.
88/// - `ctx`: Job-specific context passed to lifecycle methods.
89///
90/// # Returns
91///
92/// Returns a [`TaskResult`] consumed by the runtime.
93async fn run_job_lifecycle<T>(role: &mut T, ctx: &JobContext) -> TaskResult
94where
95    T: JobRole,
96{
97    if let Err(error) = role.init(ctx).await {
98        return job_error_to_task_result(error);
99    }
100    if let Err(error) = role.run(ctx).await {
101        return job_error_to_task_result(error);
102    }
103    if let Err(error) = role.complete(ctx).await {
104        return job_error_to_task_result(error);
105    }
106    if ctx.is_cancelled() {
107        TaskResult::Cancelled
108    } else {
109        TaskResult::Succeeded
110    }
111}
112
113/// Converts a job error into a failed task result.
114///
115/// # Arguments
116///
117/// - `error`: Job role error returned by a lifecycle method.
118///
119/// # Returns
120///
121/// Returns [`TaskResult::Failed`] with a typed task failure payload.
122fn job_error_to_task_result(error: JobError) -> TaskResult {
123    TaskResult::Failed(error.into_task_failure())
124}