use std::collections::HashMap;
use crate::model::{State, Statechart};
pub struct StateIndex<'a> {
states: HashMap<&'a str, &'a State>,
parents: HashMap<&'a str, &'a str>,
}
impl<'a> StateIndex<'a> {
pub fn new(chart: &'a Statechart) -> Self {
let estimated = chart.states.len() * 4;
let mut states = HashMap::with_capacity(estimated);
let mut parents = HashMap::with_capacity(estimated);
let limit = crate::max_depth();
build(&chart.states, &mut states, &mut parents, None, 0, limit);
Self { states, parents }
}
pub fn get(&self, id: &str) -> Option<&&'a State> {
self.states.get(id)
}
pub fn parent(&self, id: &str) -> Option<&'a str> {
self.parents.get(id).copied()
}
pub fn state_count(&self) -> usize {
self.states.len()
}
pub(crate) fn state_map(&self) -> &HashMap<&'a str, &'a State> {
&self.states
}
pub(crate) fn parent_map(&self) -> &HashMap<&'a str, &'a str> {
&self.parents
}
pub fn contains(&self, id: &str) -> bool {
self.states.contains_key(id)
}
pub fn iter(&self) -> impl Iterator<Item = (&'a str, &&'a State)> {
self.states.iter().map(|(&k, v)| (k, v))
}
}
fn build<'a>(
states: &'a [State],
state_map: &mut HashMap<&'a str, &'a State>,
parent_map: &mut HashMap<&'a str, &'a str>,
parent_id: Option<&'a str>,
depth: usize,
limit: usize,
) {
if depth > limit {
return;
}
for state in states {
state_map.insert(state.id.as_str(), state);
if let Some(pid) = parent_id {
parent_map.insert(state.id.as_str(), pid);
}
if !state.children.is_empty() {
build(
&state.children,
state_map,
parent_map,
Some(state.id.as_str()),
depth + 1,
limit,
);
}
}
}