Skip to main content

rust_supervisor/control/
command.rs

1//! Runtime control command contract.
2//!
3//! This module owns auditable command inputs and command results. Runtime code
4//! executes these commands and records state changes.
5
6use crate::control::outcome::{ChildControlResult, ChildRuntimeRecord};
7use crate::error::types::SupervisorError;
8use crate::id::types::{ChildId, SupervisorPath};
9use crate::shutdown::coordinator::ShutdownResult;
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13/// Stable identifier for an accepted control command.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct CommandId {
16    /// UUID value assigned when a command is created.
17    pub value: Uuid,
18}
19
20impl CommandId {
21    /// Creates a command identifier.
22    ///
23    /// # Arguments
24    ///
25    /// This function has no arguments.
26    ///
27    /// # Returns
28    ///
29    /// Returns a new [`CommandId`].
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// let id = rust_supervisor::control::command::CommandId::new();
35    /// assert!(!id.value.is_nil());
36    /// ```
37    pub fn new() -> Self {
38        Self {
39            value: Uuid::new_v4(),
40        }
41    }
42}
43
44impl Default for CommandId {
45    /// Creates the default command identifier.
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51/// Audit metadata attached to each runtime control command.
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct CommandMeta {
54    /// Command identifier used for audit correlation.
55    pub command_id: CommandId,
56    /// Caller that requested the command.
57    pub requested_by: String,
58    /// Human-readable command reason.
59    pub reason: String,
60}
61
62impl CommandMeta {
63    /// Creates command metadata.
64    ///
65    /// # Arguments
66    ///
67    /// - `requested_by`: Caller that requested the command.
68    /// - `reason`: Human-readable command reason.
69    ///
70    /// # Returns
71    ///
72    /// Returns a [`CommandMeta`] value with a generated command identifier.
73    pub fn new(requested_by: impl Into<String>, reason: impl Into<String>) -> Self {
74        Self {
75            command_id: CommandId::new(),
76            requested_by: requested_by.into(),
77            reason: reason.into(),
78        }
79    }
80
81    /// Validates audit metadata before command dispatch.
82    ///
83    /// # Arguments
84    ///
85    /// This function has no arguments.
86    ///
87    /// # Returns
88    ///
89    /// Returns `Ok(())` when actor and reason fields are non-empty.
90    pub(crate) fn validate(&self) -> Result<(), SupervisorError> {
91        validate_required_text(&self.requested_by, "requested_by")?;
92        validate_required_text(&self.reason, "reason")
93    }
94}
95
96/// Runtime command sent to the control loop.
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98pub enum ControlCommand {
99    /// Adds a child description under a supervisor path.
100    AddChild {
101        /// Audit metadata for the command.
102        meta: CommandMeta,
103        /// Target supervisor path.
104        target: SupervisorPath,
105        /// Child manifest text owned by the caller.
106        child_manifest: String,
107    },
108    /// Removes a child after shutting it down.
109    RemoveChild {
110        /// Audit metadata for the command.
111        meta: CommandMeta,
112        /// Target child identifier.
113        child_id: ChildId,
114    },
115    /// Restarts a child explicitly.
116    RestartChild {
117        /// Audit metadata for the command.
118        meta: CommandMeta,
119        /// Target child identifier.
120        child_id: ChildId,
121    },
122    /// Pauses automatic governance for a child.
123    PauseChild {
124        /// Audit metadata for the command.
125        meta: CommandMeta,
126        /// Target child identifier.
127        child_id: ChildId,
128    },
129    /// Resumes automatic governance for a child.
130    ResumeChild {
131        /// Audit metadata for the command.
132        meta: CommandMeta,
133        /// Target child identifier.
134        child_id: ChildId,
135    },
136    /// Quarantines a child and stops automatic restarts.
137    QuarantineChild {
138        /// Audit metadata for the command.
139        meta: CommandMeta,
140        /// Target child identifier.
141        child_id: ChildId,
142    },
143    /// Starts shutdown for the whole supervisor tree.
144    ShutdownTree {
145        /// Audit metadata for the command.
146        meta: CommandMeta,
147    },
148    /// Reads current runtime state.
149    CurrentState {
150        /// Audit metadata for the command.
151        meta: CommandMeta,
152    },
153}
154
155impl ControlCommand {
156    /// Returns audit metadata for this command.
157    ///
158    /// # Arguments
159    ///
160    /// This function has no arguments.
161    ///
162    /// # Returns
163    ///
164    /// Returns a shared reference to [`CommandMeta`].
165    pub fn meta(&self) -> &CommandMeta {
166        match self {
167            Self::AddChild { meta, .. }
168            | Self::RemoveChild { meta, .. }
169            | Self::RestartChild { meta, .. }
170            | Self::PauseChild { meta, .. }
171            | Self::ResumeChild { meta, .. }
172            | Self::QuarantineChild { meta, .. }
173            | Self::ShutdownTree { meta }
174            | Self::CurrentState { meta } => meta,
175        }
176    }
177
178    /// Validates audit metadata attached to this command.
179    ///
180    /// # Arguments
181    ///
182    /// This function has no arguments.
183    ///
184    /// # Returns
185    ///
186    /// Returns `Ok(())` when the command carries auditable metadata.
187    pub(crate) fn validate_audit_metadata(&self) -> Result<(), SupervisorError> {
188        self.meta().validate()
189    }
190}
191
192/// Validates one required text field.
193///
194/// # Arguments
195///
196/// - `value`: Text value supplied by the command caller.
197/// - `field`: Field name used in the diagnostic message.
198///
199/// # Returns
200///
201/// Returns `Ok(())` when the value is not blank.
202fn validate_required_text(value: &str, field: &str) -> Result<(), SupervisorError> {
203    if value.trim().is_empty() {
204        return Err(SupervisorError::InvalidTransition {
205            message: format!("control command {field} must not be empty"),
206        });
207    }
208    Ok(())
209}
210
211/// Current runtime state returned by `current_state`.
212#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
213pub struct CurrentState {
214    /// Number of children known to the control loop.
215    pub child_count: usize,
216    /// Whether tree shutdown has completed.
217    pub shutdown_completed: bool,
218    /// Runtime state records for declared children.
219    pub child_runtime_records: Vec<ChildRuntimeRecord>,
220}
221
222/// Result returned after a control command is executed.
223#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
224pub enum CommandResult {
225    /// Child was accepted by the control loop.
226    ChildAdded {
227        /// Child manifest stored by the runtime.
228        child_manifest: String,
229    },
230    /// Child control result after a command.
231    ChildControl {
232        /// Outcome produced by the control command.
233        outcome: ChildControlResult,
234    },
235    /// Current state query result.
236    CurrentState {
237        /// Runtime current state.
238        state: CurrentState,
239    },
240    /// Shutdown command result.
241    Shutdown {
242        /// Shutdown phase and cause.
243        result: ShutdownResult,
244    },
245}