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}