use crate::error::{RuntimeError, RuntimeResult};
use crate::ids::{EventName, StateName};
use crate::state::{StateMachine, StateTransition};
use parking_lot::Mutex;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuntimeLifecycleState {
Booting,
LoadingConfig,
CheckingSecurity,
OpeningStorage,
StartingApi,
Running,
Degraded,
Restricted,
ShuttingDown,
Stopped,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuntimeLifecycleEvent {
ConfigLoaded,
SecurityChecked,
StorageOpened,
ApiStarted,
DegradedDetected,
RestrictedDetected,
ShutdownRequested,
ShutdownCompleted,
RecoveryCompleted,
}
#[derive(Debug)]
pub struct RuntimeLifecycle {
machine: Mutex<StateMachine>,
}
impl Clone for RuntimeLifecycle {
fn clone(&self) -> Self {
let guard = self.machine.lock();
Self {
machine: Mutex::new(guard.clone()),
}
}
}
impl RuntimeLifecycleState {
fn as_state_name(self) -> StateName {
StateName::new(match self {
RuntimeLifecycleState::Booting => "Booting",
RuntimeLifecycleState::LoadingConfig => "LoadingConfig",
RuntimeLifecycleState::CheckingSecurity => "CheckingSecurity",
RuntimeLifecycleState::OpeningStorage => "OpeningStorage",
RuntimeLifecycleState::StartingApi => "StartingApi",
RuntimeLifecycleState::Running => "Running",
RuntimeLifecycleState::Degraded => "Degraded",
RuntimeLifecycleState::Restricted => "Restricted",
RuntimeLifecycleState::ShuttingDown => "ShuttingDown",
RuntimeLifecycleState::Stopped => "Stopped",
})
.unwrap()
}
fn from_state_name(state: &StateName) -> RuntimeResult<Self> {
match state.as_str() {
"Booting" => Ok(Self::Booting),
"LoadingConfig" => Ok(Self::LoadingConfig),
"CheckingSecurity" => Ok(Self::CheckingSecurity),
"OpeningStorage" => Ok(Self::OpeningStorage),
"StartingApi" => Ok(Self::StartingApi),
"Running" => Ok(Self::Running),
"Degraded" => Ok(Self::Degraded),
"Restricted" => Ok(Self::Restricted),
"ShuttingDown" => Ok(Self::ShuttingDown),
"Stopped" => Ok(Self::Stopped),
_ => Err(RuntimeError::InvalidStateTransition),
}
}
}
impl RuntimeLifecycleEvent {
fn as_event_name(self) -> EventName {
EventName::new(match self {
RuntimeLifecycleEvent::ConfigLoaded => "ConfigLoaded",
RuntimeLifecycleEvent::SecurityChecked => "SecurityChecked",
RuntimeLifecycleEvent::StorageOpened => "StorageOpened",
RuntimeLifecycleEvent::ApiStarted => "ApiStarted",
RuntimeLifecycleEvent::DegradedDetected => "DegradedDetected",
RuntimeLifecycleEvent::RestrictedDetected => "RestrictedDetected",
RuntimeLifecycleEvent::ShutdownRequested => "ShutdownRequested",
RuntimeLifecycleEvent::ShutdownCompleted => "ShutdownCompleted",
RuntimeLifecycleEvent::RecoveryCompleted => "RecoveryCompleted",
})
.unwrap()
}
}
impl RuntimeLifecycle {
pub fn new() -> Self {
let mut machine = StateMachine::new(RuntimeLifecycleState::Booting.as_state_name());
let transitions = vec![
(
RuntimeLifecycleState::Booting,
RuntimeLifecycleEvent::ConfigLoaded,
RuntimeLifecycleState::CheckingSecurity,
),
(
RuntimeLifecycleState::CheckingSecurity,
RuntimeLifecycleEvent::SecurityChecked,
RuntimeLifecycleState::OpeningStorage,
),
(
RuntimeLifecycleState::OpeningStorage,
RuntimeLifecycleEvent::StorageOpened,
RuntimeLifecycleState::StartingApi,
),
(
RuntimeLifecycleState::StartingApi,
RuntimeLifecycleEvent::ApiStarted,
RuntimeLifecycleState::Running,
),
(
RuntimeLifecycleState::Running,
RuntimeLifecycleEvent::DegradedDetected,
RuntimeLifecycleState::Degraded,
),
(
RuntimeLifecycleState::Running,
RuntimeLifecycleEvent::RestrictedDetected,
RuntimeLifecycleState::Restricted,
),
(
RuntimeLifecycleState::Degraded,
RuntimeLifecycleEvent::RecoveryCompleted,
RuntimeLifecycleState::Running,
),
(
RuntimeLifecycleState::Restricted,
RuntimeLifecycleEvent::RecoveryCompleted,
RuntimeLifecycleState::Running,
),
(
RuntimeLifecycleState::Running,
RuntimeLifecycleEvent::ShutdownRequested,
RuntimeLifecycleState::ShuttingDown,
),
(
RuntimeLifecycleState::Degraded,
RuntimeLifecycleEvent::ShutdownRequested,
RuntimeLifecycleState::ShuttingDown,
),
(
RuntimeLifecycleState::Restricted,
RuntimeLifecycleEvent::ShutdownRequested,
RuntimeLifecycleState::ShuttingDown,
),
(
RuntimeLifecycleState::ShuttingDown,
RuntimeLifecycleEvent::ShutdownCompleted,
RuntimeLifecycleState::Stopped,
),
];
for (from, event, to) in transitions {
let _ = machine.add_transition(StateTransition {
from: from.as_state_name(),
event: event.as_event_name(),
to: to.as_state_name(),
});
}
Self {
machine: Mutex::new(machine),
}
}
pub fn current(&self) -> RuntimeLifecycleState {
let guard = self.machine.lock();
match RuntimeLifecycleState::from_state_name(guard.current()) {
Ok(state) => state,
Err(_) => RuntimeLifecycleState::Booting,
}
}
pub fn apply(&self, event: RuntimeLifecycleEvent) -> RuntimeResult<RuntimeLifecycleState> {
let mut guard = self.machine.lock();
let next = guard.apply(&event.as_event_name())?;
RuntimeLifecycleState::from_state_name(next)
}
pub fn is_running(&self) -> bool {
self.current() == RuntimeLifecycleState::Running
}
pub fn is_stopped(&self) -> bool {
self.current() == RuntimeLifecycleState::Stopped
}
pub fn is_restricted(&self) -> bool {
self.current() == RuntimeLifecycleState::Restricted
}
}
impl Default for RuntimeLifecycle {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[path = "lifecycle_tests.rs"]
mod tests;