///|
pub(all) struct Transition {
from : String
event : String
to : String
action : String
}
///|
pub(all) struct TransitionResult {
accepted : Bool
from : String
event : String
to : String
action : String
reason : String
}
///|
pub(all) struct StateMachine {
name : String
mut state : String
mut transitions : Array[Transition]
mut history : Array[TransitionResult]
}
///|
pub fn transition(
from : String,
event : String,
to : String,
action? : String = "",
) -> Transition {
{ from, event, to, action }
}
///|
pub fn StateMachine::new(name : String, initial : String) -> StateMachine {
{ name, state: initial, transitions: [], history: [] }
}
///|
pub fn StateMachine::add(
self : StateMachine,
transition : Transition,
) -> StateMachine {
self.transitions.push(transition)
self
}
///|
pub fn StateMachine::state(self : StateMachine) -> String {
self.state
}
///|
pub fn StateMachine::send(
self : StateMachine,
event : String,
) -> TransitionResult {
for transition in self.transitions {
if transition.from == self.state && transition.event == event {
let from = self.state
self.state = transition.to
let result = {
accepted: true,
from,
event,
to: transition.to,
action: transition.action,
reason: "accepted",
}
self.history.push(result)
return result
}
}
let rejected = {
accepted: false,
from: self.state,
event,
to: self.state,
action: "",
reason: "no transition",
}
self.history.push(rejected)
rejected
}
///|
pub fn StateMachine::history(self : StateMachine) -> Array[TransitionResult] {
self.history.copy()
}
///|
pub fn Sim::record_transition(
self : Sim,
machine : StateMachine,
result : TransitionResult,
) -> Unit {
let status = if result.accepted { "accepted" } else { "rejected" }
self.record(
0,
"state.transition",
machine.name +
":" +
result.from +
"-" +
result.event +
"->" +
result.to +
":" +
status,
)
if result.accepted {
self.inc_counter("state.accepted")
} else {
self.inc_counter("state.rejected")
}
}
///|
/// Projects accepted and rejected transitions into a causally linked event stream.
pub fn StateMachine::event_stream(self : StateMachine) -> EventStream {
let stream = EventStream::new()
let mut parent_id = 0
let mut tick = 0
for result in self.history {
let event = stream.record(
StateTransition,
tick,
result.event,
correlation_id=self.name,
source=result.from,
target=result.to,
parent_id~,
payload=result.action,
failed=!result.accepted,
)
parent_id = event.id
tick += 1
}
stream
}