use crate::run::state::RunState;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunTransitionError {
InvalidTransition {
from: RunState,
to: RunState,
},
}
impl std::fmt::Display for RunTransitionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidTransition { from, to } => {
write!(f, "invalid run transition: {from:?} -> {to:?}")
}
}
}
}
impl std::error::Error for RunTransitionError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use]
pub struct Transition {
from: RunState,
to: RunState,
}
impl Transition {
pub fn new(from: RunState, to: RunState) -> Result<Self, RunTransitionError> {
if is_valid_transition(from, to) {
Ok(Transition { from, to })
} else {
Err(RunTransitionError::InvalidTransition { from, to })
}
}
pub fn from(&self) -> RunState {
self.from
}
pub fn to(&self) -> RunState {
self.to
}
}
pub fn is_valid_transition(from: RunState, to: RunState) -> bool {
if from.is_terminal() {
return false;
}
matches!(
(from, to),
(RunState::Scheduled, RunState::Ready)
| (RunState::Scheduled, RunState::Canceled)
| (RunState::Ready, RunState::Leased)
| (RunState::Ready, RunState::Canceled)
| (RunState::Leased, RunState::Running)
| (RunState::Leased, RunState::Ready)
| (RunState::Leased, RunState::Canceled)
| (RunState::Running, RunState::RetryWait)
| (RunState::Running, RunState::Suspended)
| (RunState::Running, RunState::Completed)
| (RunState::Running, RunState::Failed)
| (RunState::Running, RunState::Canceled)
| (RunState::RetryWait, RunState::Ready)
| (RunState::RetryWait, RunState::Failed)
| (RunState::RetryWait, RunState::Canceled)
| (RunState::Suspended, RunState::Ready)
| (RunState::Suspended, RunState::Canceled)
)
}
pub fn valid_transitions(from: RunState) -> Vec<RunState> {
let states = [
RunState::Scheduled,
RunState::Ready,
RunState::Leased,
RunState::Running,
RunState::RetryWait,
RunState::Suspended,
RunState::Completed,
RunState::Failed,
RunState::Canceled,
];
states.into_iter().filter(|&to| is_valid_transition(from, to)).collect()
}