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}