use compact_str::CompactString;
use serde::{Deserialize, Serialize};
use super::datamodel::DataModel;
use super::state::State;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub struct Statechart {
pub name: Option<CompactString>,
pub initial: CompactString,
pub states: Vec<State>,
#[serde(default)]
pub datamodel: DataModel,
#[serde(default)]
pub binding: Binding,
#[serde(default = "default_version")]
pub version: CompactString,
#[serde(default = "default_xmlns")]
pub xmlns: CompactString,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub definition_version: Option<CompactString>,
}
fn default_version() -> CompactString {
CompactString::const_new("1.0")
}
fn default_xmlns() -> CompactString {
CompactString::const_new("http://www.w3.org/2005/07/scxml")
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[serde(rename_all = "snake_case")]
pub enum Binding {
#[default]
Early,
Late,
}
impl Statechart {
pub fn new(initial: impl Into<CompactString>, states: Vec<State>) -> Self {
Self {
name: None,
initial: initial.into(),
states,
datamodel: DataModel::default(),
binding: Binding::default(),
version: default_version(),
xmlns: default_xmlns(),
definition_version: None,
}
}
pub fn with_name(mut self, name: impl Into<CompactString>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_datamodel(mut self, datamodel: DataModel) -> Self {
self.datamodel = datamodel;
self
}
pub fn iter_all_states(&self) -> impl Iterator<Item = &State> {
self.states.iter().flat_map(|s| s.iter_all())
}
pub fn all_state_ids(&self) -> Vec<&CompactString> {
let estimate = self.states.len() * 4; let mut ids = Vec::with_capacity(estimate);
ids.extend(self.iter_all_states().map(|s| &s.id));
ids
}
pub fn find_state(&self, id: &str) -> Option<&State> {
self.iter_all_states().find(|s| s.id.as_str() == id)
}
}
impl std::fmt::Display for Statechart {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = self.name.as_deref().unwrap_or("unnamed");
let mut total = 0usize;
let mut transitions = 0usize;
for state in self.iter_all_states() {
total += 1;
transitions += state.transitions.len();
}
write!(
f,
"Statechart({name}, {total} states, {transitions} transitions, initial={initial})",
initial = self.initial,
)
}
}