///|
/// Retrieves the current state of the engine.
pub fn[S, E, Ctx] Engine::state(self : Engine[S, E, Ctx]) -> S {
self.current_state
}
///|
/// Retrieves the current context of the engine.
pub fn[S, E, Ctx] Engine::context(self : Engine[S, E, Ctx]) -> Ctx {
self.context
}
///|
/// Returns the successful transition history.
pub fn[S, E, Ctx] Engine::history(
self : Engine[S, E, Ctx],
) -> Array[TransitionRecord[S, E]] {
self.history_entries.copy()
}
///|
/// Returns the most recent transition error, if any.
pub fn[S, E, Ctx] Engine::last_error(
self : Engine[S, E, Ctx],
) -> TransitionError? {
self.last_error_value
}
///|
fn[S, E, Ctx] fail_with(
engine : Engine[S, E, Ctx],
current : S,
event : E,
err : TransitionError,
) -> Result[Unit, TransitionError] {
engine.last_error_value = Some(err)
engine.record_failure(current, event, err)
Err(err)
}
///|
/// Sends an event to the state machine engine to trigger a transition.
pub fn[S : Hash + Eq, E : Hash + Eq, Ctx] Engine::send(
self : Engine[S, E, Ctx],
event : E,
) -> Result[Unit, String] {
match self.try_send(event) {
Ok(_) => Ok(())
Err(err) => Err(format_transition_error(err))
}
}
///|
/// Attempts a transition and returns a structured transition error on failure.
pub fn[S : Hash + Eq, E : Hash + Eq, Ctx] Engine::try_send(
self : Engine[S, E, Ctx],
event : E,
) -> Result[Unit, TransitionError] {
match self.build_error {
Some(err) => return fail_with(self, self.current_state, event, err)
None => ()
}
let current = self.current_state
match self.transitions.get(current) {
None => fail_with(self, current, event, NoTransitionsForCurrentState)
Some(state_map) =>
match state_map.get(event) {
None => fail_with(self, current, event, EventNotHandledInCurrentState)
Some(next_state) => {
let guard_fn_opt = match self.guards.get(current) {
Some(state_guards) => state_guards.get(event)
None => None
}
let action_fn_opt = match self.actions.get(current) {
Some(state_actions) => state_actions.get(event)
None => None
}
let used_guard = guard_fn_opt is Some(_)
let used_action = action_fn_opt is Some(_)
let allowed = match guard_fn_opt {
Some(guard_fn) => guard_fn(current, event, self.context)
None => true
}
if !allowed {
return fail_with(self, current, event, GuardRejected)
}
match action_fn_opt {
Some(action_fn) =>
self.context = action_fn(current, event, self.context)
None => ()
}
match self.on_exit.get(current) {
Some(cb) => {
self.record_hook()
cb(current, event, self.context)
}
None => ()
}
self.current_state = next_state
match self.on_enter.get(next_state) {
Some(cb) => {
self.record_hook()
cb(next_state, event, self.context)
}
None => ()
}
let history_index = self.history_entries.length()
self.history_entries.push({
from: current,
event,
to: next_state,
used_guard,
used_action,
})
self.record_success(current, event, next_state, history_index)
self.last_error_value = None
Ok(())
}
}
}
}