use compact_str::CompactString;
use serde::{Deserialize, Serialize};
use crate::model::state::StateKind;
use crate::model::{State, Statechart};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct FlatState {
pub id: CompactString,
pub kind: StateKind,
pub parent: Option<CompactString>,
pub initial: bool,
pub depth: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct FlatTransition {
pub source: CompactString,
pub target: CompactString,
pub event: Option<CompactString>,
pub guard: Option<CompactString>,
}
pub fn flatten(chart: &Statechart) -> (Vec<FlatState>, Vec<FlatTransition>) {
let (state_count, trans_count) = {
let mut sc = 0;
let mut tc = 0;
for s in chart.iter_all_states() {
sc += 1;
tc += s.transitions.len();
}
(sc, tc)
};
let mut states = Vec::with_capacity(state_count);
let mut transitions = Vec::with_capacity(trans_count);
let limit = crate::max_depth();
for state in &chart.states {
flatten_state(
state,
None,
0,
&chart.initial,
&mut states,
&mut transitions,
limit,
);
}
(states, transitions)
}
fn flatten_state(
state: &State,
parent: Option<&CompactString>,
depth: u32,
chart_initial: &CompactString,
states: &mut Vec<FlatState>,
transitions: &mut Vec<FlatTransition>,
limit: usize,
) {
if depth as usize > limit {
return;
}
let is_initial = state.id == *chart_initial;
states.push(FlatState {
id: state.id.clone(),
kind: state.kind,
parent: parent.cloned(),
initial: is_initial,
depth,
});
for t in &state.transitions {
for target in &t.targets {
transitions.push(FlatTransition {
source: state.id.clone(),
target: target.clone(),
event: t.event.clone(),
guard: t.guard.clone(),
});
}
}
for child in &state.children {
flatten_state(
child,
Some(&state.id),
depth + 1,
chart_initial,
states,
transitions,
limit,
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::{State, Transition};
#[test]
fn flatten_simple_chart() {
let chart = Statechart::new(
"a",
vec![
{
let mut s = State::atomic("a");
s.transitions.push(Transition::new("go", "b"));
s
},
{
let mut s = State::atomic("b");
s.transitions.push(Transition::new("done", "end"));
s
},
State::final_state("end"),
],
);
let (states, transitions) = flatten(&chart);
assert_eq!(states.len(), 3);
assert_eq!(transitions.len(), 2);
assert!(states[0].initial);
assert_eq!(states[0].depth, 0);
}
#[test]
fn flatten_nested_chart() {
let chart = Statechart::new(
"main",
vec![State::compound(
"main",
"child_a",
vec![
{
let mut s = State::atomic("child_a");
s.transitions.push(Transition::new("next", "child_b"));
s
},
State::atomic("child_b"),
],
)],
);
let (states, _) = flatten(&chart);
assert_eq!(states.len(), 3); assert_eq!(states[0].depth, 0);
assert_eq!(states[1].depth, 1);
assert_eq!(states[1].parent.as_deref(), Some("main"));
}
}