use crate::return_code::ReturnCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LifeCycleState {
Created,
Inactive,
Active,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExecutionKind {
Periodic,
EventDriven,
Other,
}
pub trait ComponentAction {
fn on_initialize(&mut self) -> ReturnCode {
ReturnCode::Ok
}
fn on_finalize(&mut self) -> ReturnCode {
ReturnCode::Ok
}
fn on_startup(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
fn on_shutdown(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
fn on_activated(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
fn on_deactivated(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
fn on_aborting(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
fn on_error(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
fn on_reset(&mut self, _exec_handle: u32) -> ReturnCode {
ReturnCode::Ok
}
}
#[must_use]
pub const fn is_valid_transition(from: LifeCycleState, to: LifeCycleState) -> bool {
use LifeCycleState::{Active, Created, Error, Inactive};
matches!(
(from, to),
(Created, Inactive)
| (Inactive, Active)
| (Active, Inactive)
| (Active, Error)
| (Error, Inactive)
| (Error, Error)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_transitions_match_spec_state_machine() {
for (from, to, expected) in [
(LifeCycleState::Created, LifeCycleState::Inactive, true),
(LifeCycleState::Inactive, LifeCycleState::Active, true),
(LifeCycleState::Active, LifeCycleState::Inactive, true),
(LifeCycleState::Active, LifeCycleState::Error, true),
(LifeCycleState::Error, LifeCycleState::Inactive, true),
(LifeCycleState::Created, LifeCycleState::Active, false),
(LifeCycleState::Created, LifeCycleState::Error, false),
(LifeCycleState::Inactive, LifeCycleState::Created, false),
(LifeCycleState::Inactive, LifeCycleState::Error, false),
(LifeCycleState::Active, LifeCycleState::Created, false),
(LifeCycleState::Error, LifeCycleState::Active, false),
(LifeCycleState::Error, LifeCycleState::Created, false),
] {
assert_eq!(
is_valid_transition(from, to),
expected,
"{from:?} -> {to:?}"
);
}
}
#[test]
fn default_component_action_returns_ok_for_all_callbacks() {
struct Stub;
impl ComponentAction for Stub {}
let mut s = Stub;
assert_eq!(s.on_initialize(), ReturnCode::Ok);
assert_eq!(s.on_finalize(), ReturnCode::Ok);
assert_eq!(s.on_startup(0), ReturnCode::Ok);
assert_eq!(s.on_shutdown(0), ReturnCode::Ok);
assert_eq!(s.on_activated(0), ReturnCode::Ok);
assert_eq!(s.on_deactivated(0), ReturnCode::Ok);
assert_eq!(s.on_aborting(0), ReturnCode::Ok);
assert_eq!(s.on_error(0), ReturnCode::Ok);
assert_eq!(s.on_reset(0), ReturnCode::Ok);
}
#[test]
fn execution_kind_distinguishes_three_modes() {
let kinds = [
ExecutionKind::Periodic,
ExecutionKind::EventDriven,
ExecutionKind::Other,
];
assert_ne!(kinds[0], kinds[1]);
assert_ne!(kinds[1], kinds[2]);
assert_ne!(kinds[0], kinds[2]);
}
#[test]
fn error_self_loop_allowed() {
assert!(is_valid_transition(
LifeCycleState::Error,
LifeCycleState::Error
));
}
}