use crate::config::state::ConfigState;
use crate::control::handle::SupervisorHandle;
use crate::dashboard::config::validate_dashboard_ipc_config;
use crate::dashboard::error::DashboardError;
use crate::dashboard::runtime::start_dashboard_ipc_runtime;
use crate::error::types::SupervisorError;
use crate::runtime::control_loop::{RuntimeControlState, run_control_loop};
use crate::shutdown::stage::ShutdownPolicy;
use crate::spec::supervisor::SupervisorSpec;
use std::path::Path;
use tokio::sync::{broadcast, mpsc};
#[derive(Debug, Clone, Copy, Default)]
pub struct Supervisor;
impl Supervisor {
pub async fn start(spec: SupervisorSpec) -> Result<SupervisorHandle, SupervisorError> {
let shutdown_policy = shutdown_policy_from_spec(&spec);
Self::start_with_policy(spec, shutdown_policy).await
}
pub async fn start_from_config_state(
state: ConfigState,
) -> Result<SupervisorHandle, SupervisorError> {
let ipc_config = state.ipc.clone();
let spec = state.to_supervisor_spec()?;
let mut handle = Self::start(spec.clone()).await?;
let dashboard_config =
validate_dashboard_ipc_config(ipc_config.as_ref()).map_err(dashboard_startup_error)?;
if let Some(dashboard_config) = dashboard_config {
let dashboard_runtime =
start_dashboard_ipc_runtime(dashboard_config, spec, handle.clone())
.map_err(dashboard_startup_error)?;
handle = handle.with_dashboard_runtime(dashboard_runtime);
}
Ok(handle)
}
pub async fn start_from_config_file(
path: impl AsRef<Path>,
) -> Result<SupervisorHandle, SupervisorError> {
let state = crate::config::loader::load_config_state(path)?;
Self::start_from_config_state(state).await
}
pub async fn start_with_policy(
spec: SupervisorSpec,
shutdown_policy: ShutdownPolicy,
) -> Result<SupervisorHandle, SupervisorError> {
spec.validate()?;
let (command_sender, command_receiver) = mpsc::channel(spec.control_channel_capacity);
let (event_sender, _) = broadcast::channel(spec.event_channel_capacity);
let state = RuntimeControlState::new(spec, shutdown_policy, command_sender.clone())?;
tokio::spawn(run_control_loop(
state,
command_receiver,
event_sender.clone(),
));
Ok(SupervisorHandle::new(command_sender, event_sender))
}
}
fn shutdown_policy_from_spec(spec: &SupervisorSpec) -> ShutdownPolicy {
ShutdownPolicy::new(
spec.default_shutdown_policy.graceful_timeout,
spec.default_shutdown_policy.abort_wait,
true,
)
}
fn dashboard_startup_error(error: DashboardError) -> SupervisorError {
SupervisorError::fatal_config(format!("dashboard IPC startup failed: {error}"))
}