use crate::control::outcome::{ChildControlResult, ChildRuntimeRecord};
use crate::error::types::SupervisorError;
use crate::id::types::{ChildId, SupervisorPath};
use crate::shutdown::coordinator::ShutdownResult;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CommandId {
pub value: Uuid,
}
impl CommandId {
pub fn new() -> Self {
Self {
value: Uuid::new_v4(),
}
}
}
impl Default for CommandId {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommandMeta {
pub command_id: CommandId,
pub requested_by: String,
pub reason: String,
}
impl CommandMeta {
pub fn new(requested_by: impl Into<String>, reason: impl Into<String>) -> Self {
Self {
command_id: CommandId::new(),
requested_by: requested_by.into(),
reason: reason.into(),
}
}
pub(crate) fn validate(&self) -> Result<(), SupervisorError> {
validate_required_text(&self.requested_by, "requested_by")?;
validate_required_text(&self.reason, "reason")
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ControlCommand {
AddChild {
meta: CommandMeta,
target: SupervisorPath,
child_manifest: String,
},
RemoveChild {
meta: CommandMeta,
child_id: ChildId,
},
RestartChild {
meta: CommandMeta,
child_id: ChildId,
},
PauseChild {
meta: CommandMeta,
child_id: ChildId,
},
ResumeChild {
meta: CommandMeta,
child_id: ChildId,
},
QuarantineChild {
meta: CommandMeta,
child_id: ChildId,
},
ShutdownTree {
meta: CommandMeta,
},
CurrentState {
meta: CommandMeta,
},
}
impl ControlCommand {
pub fn meta(&self) -> &CommandMeta {
match self {
Self::AddChild { meta, .. }
| Self::RemoveChild { meta, .. }
| Self::RestartChild { meta, .. }
| Self::PauseChild { meta, .. }
| Self::ResumeChild { meta, .. }
| Self::QuarantineChild { meta, .. }
| Self::ShutdownTree { meta }
| Self::CurrentState { meta } => meta,
}
}
pub(crate) fn validate_audit_metadata(&self) -> Result<(), SupervisorError> {
self.meta().validate()
}
}
fn validate_required_text(value: &str, field: &str) -> Result<(), SupervisorError> {
if value.trim().is_empty() {
return Err(SupervisorError::InvalidTransition {
message: format!("control command {field} must not be empty"),
});
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CurrentState {
pub child_count: usize,
pub shutdown_completed: bool,
pub child_runtime_records: Vec<ChildRuntimeRecord>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CommandResult {
ChildAdded {
child_manifest: String,
},
ChildControl {
outcome: ChildControlResult,
},
CurrentState {
state: CurrentState,
},
Shutdown {
result: ShutdownResult,
},
}