use serde::{Deserialize, Serialize};
use crate::model::{StateKind, Statechart};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatechartStats {
pub total_states: usize,
pub atomic_states: usize,
pub compound_states: usize,
pub parallel_states: usize,
pub final_states: usize,
pub history_states: usize,
pub total_transitions: usize,
pub guarded_transitions: usize,
pub deadline_transitions: usize,
pub max_depth: usize,
pub data_items: usize,
pub total_actions: usize,
}
pub fn stats(chart: &Statechart) -> StatechartStats {
let mut s = StatechartStats {
total_states: 0,
atomic_states: 0,
compound_states: 0,
parallel_states: 0,
final_states: 0,
history_states: 0,
total_transitions: 0,
guarded_transitions: 0,
deadline_transitions: 0,
max_depth: 0,
data_items: chart.datamodel.items.len(),
total_actions: 0,
};
let limit = crate::max_depth();
for state in &chart.states {
collect_stats(state, 0, limit, &mut s);
}
s
}
fn collect_stats(state: &crate::model::State, depth: usize, limit: usize, s: &mut StatechartStats) {
if depth > limit {
return;
}
s.total_states += 1;
if depth > s.max_depth {
s.max_depth = depth;
}
match state.kind {
StateKind::Atomic => s.atomic_states += 1,
StateKind::Compound => s.compound_states += 1,
StateKind::Parallel => s.parallel_states += 1,
StateKind::Final => s.final_states += 1,
StateKind::History(_) => s.history_states += 1,
}
for t in &state.transitions {
s.total_transitions += 1;
if t.guard.is_some() {
s.guarded_transitions += 1;
}
if t.delay.is_some() {
s.deadline_transitions += 1;
}
count_actions(&t.actions, s);
}
count_actions(&state.on_entry, s);
count_actions(&state.on_exit, s);
for child in &state.children {
collect_stats(child, depth + 1, limit, s);
}
}
fn count_actions(actions: &[crate::model::Action], s: &mut StatechartStats) {
for action in actions {
s.total_actions += 1;
match &action.kind {
crate::model::ActionKind::If { actions, .. } => count_actions(actions, s),
crate::model::ActionKind::Foreach { actions, .. } => count_actions(actions, s),
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::{State, Transition};
#[test]
fn stats_simple_chart() {
let chart = Statechart::new(
"a",
vec![
{
let mut s = State::atomic("a");
s.transitions
.push(Transition::new("go", "b").with_guard("ready"));
s
},
{
let mut s = State::atomic("b");
s.transitions.push(Transition::new("done", "end"));
s
},
State::final_state("end"),
],
);
let s = stats(&chart);
assert_eq!(s.total_states, 3);
assert_eq!(s.atomic_states, 2);
assert_eq!(s.final_states, 1);
assert_eq!(s.total_transitions, 2);
assert_eq!(s.guarded_transitions, 1);
assert_eq!(s.max_depth, 0);
}
#[test]
fn stats_nested_chart() {
let chart = Statechart::new(
"main",
vec![State::compound(
"main",
"child",
vec![
{
let mut s = State::atomic("child");
s.transitions.push(Transition::new("done", "end"));
s
},
State::final_state("end"),
],
)],
);
let s = stats(&chart);
assert_eq!(s.total_states, 3); assert_eq!(s.compound_states, 1);
assert_eq!(s.max_depth, 1);
}
}