use super::error::{LifecycleError, Result};
use super::state::LifecycleState;
use std::marker::PhantomData;
pub struct Initial;
pub struct Initialized;
pub struct Setup;
pub struct Built;
pub struct Tested;
pub struct Deployed;
#[derive(Debug, Clone)]
pub struct LifecycleStateMachine<State> {
pub(crate) state: LifecycleState,
pub(crate) _marker: PhantomData<State>,
}
impl LifecycleStateMachine<Initial> {
pub fn new() -> Self {
Self {
state: LifecycleState::default(),
_marker: PhantomData,
}
}
pub fn init(self) -> Result<LifecycleStateMachine<Initialized>> {
Ok(LifecycleStateMachine {
state: self.state,
_marker: PhantomData,
})
}
}
impl LifecycleStateMachine<Initialized> {
pub fn setup(self) -> Result<LifecycleStateMachine<Setup>> {
if !self.state.has_completed_phase("init") {
return Err(LifecycleError::Other(
"Cannot run setup: init phase not completed".into(),
));
}
Ok(LifecycleStateMachine {
state: self.state,
_marker: PhantomData,
})
}
}
impl LifecycleStateMachine<Setup> {
pub fn build(self) -> Result<LifecycleStateMachine<Built>> {
if !self.state.has_completed_phase("setup") {
return Err(LifecycleError::Other(
"Cannot run build: setup phase not completed".into(),
));
}
Ok(LifecycleStateMachine {
state: self.state,
_marker: PhantomData,
})
}
}
impl LifecycleStateMachine<Built> {
pub fn test(self) -> Result<LifecycleStateMachine<Tested>> {
if !self.state.has_completed_phase("build") {
return Err(LifecycleError::Other(
"Cannot run test: build phase not completed".into(),
));
}
Ok(LifecycleStateMachine {
state: self.state,
_marker: PhantomData,
})
}
}
impl LifecycleStateMachine<Tested> {
pub fn deploy(self) -> Result<LifecycleStateMachine<Deployed>> {
if !self.state.has_completed_phase("test") {
return Err(LifecycleError::Other(
"Cannot run deploy: test phase not completed".into(),
));
}
Ok(LifecycleStateMachine {
state: self.state,
_marker: PhantomData,
})
}
}
impl<State> LifecycleStateMachine<State> {
pub fn state(&self) -> &LifecycleState {
&self.state
}
pub fn state_mut(&mut self) -> &mut LifecycleState {
&mut self.state
}
pub fn has_completed_phase(&self, phase: &str) -> bool {
self.state.phase_history.iter().any(|r| r.phase == phase)
}
pub fn last_phase(&self) -> Option<&str> {
self.state.last_phase.as_deref()
}
}
impl Default for LifecycleStateMachine<Initial> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_state_transitions() {
let mut lifecycle = LifecycleStateMachine::<Initial>::new();
lifecycle
.state_mut()
.record_run("init".to_string(), 0, 100, true);
let lifecycle = lifecycle.init().unwrap();
let mut lifecycle = LifecycleStateMachine::<Initialized> {
state: lifecycle.state().clone(),
_marker: PhantomData,
};
lifecycle
.state_mut()
.record_run("setup".to_string(), 100, 200, true);
let lifecycle = lifecycle.setup().unwrap();
let mut lifecycle = LifecycleStateMachine::<Setup> {
state: lifecycle.state().clone(),
_marker: PhantomData,
};
lifecycle
.state_mut()
.record_run("build".to_string(), 200, 300, true);
let lifecycle = lifecycle.build().unwrap();
let mut lifecycle = LifecycleStateMachine::<Built> {
state: lifecycle.state().clone(),
_marker: PhantomData,
};
lifecycle
.state_mut()
.record_run("test".to_string(), 300, 400, true);
let lifecycle = lifecycle.test().unwrap();
let mut lifecycle = LifecycleStateMachine::<Tested> {
state: lifecycle.state().clone(),
_marker: PhantomData,
};
lifecycle
.state_mut()
.record_run("deploy".to_string(), 400, 500, true);
let lifecycle = lifecycle.deploy().unwrap();
assert!(lifecycle.has_completed_phase("init"));
assert!(lifecycle.has_completed_phase("setup"));
assert!(lifecycle.has_completed_phase("build"));
assert!(lifecycle.has_completed_phase("test"));
assert!(lifecycle.has_completed_phase("deploy"));
}
#[test]
fn test_state_access() {
let lifecycle = LifecycleStateMachine::<Initial>::new();
assert_eq!(lifecycle.last_phase(), None);
assert!(!lifecycle.has_completed_phase("init"));
}
#[test]
fn test_invalid_transition_validation() {
let lifecycle = LifecycleStateMachine::<Initial>::new();
let mut lifecycle = lifecycle;
lifecycle
.state_mut()
.record_run("init".to_string(), 0, 100, true);
let lifecycle = lifecycle.init().unwrap();
let lifecycle = LifecycleStateMachine::<Initialized> {
state: lifecycle.state().clone(),
_marker: PhantomData,
};
let _lifecycle = lifecycle.setup().unwrap(); }
}