Skip to main content

rust_supervisor/role/adapter/
sidecar.rs

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