use compact_str::CompactString;
use serde::{Deserialize, Serialize};
use super::action::Action;
use super::transition::Transition;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(
feature = "rkyv",
rkyv(
serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator),
deserialize_bounds(__D::Error: rkyv::rancor::Source),
bytecheck(bounds(
__C: rkyv::validation::ArchiveContext,
<__C as rkyv::rancor::Fallible>::Error: rkyv::rancor::Source,
)),
)
)]
pub struct State {
pub id: CompactString,
pub kind: StateKind,
#[serde(default)]
pub transitions: Vec<Transition>,
#[serde(default)]
pub on_entry: Vec<Action>,
#[serde(default)]
pub on_exit: Vec<Action>,
#[serde(default)]
#[cfg_attr(feature = "rkyv", rkyv(omit_bounds))]
pub children: Vec<State>,
#[serde(default)]
pub initial: Option<CompactString>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[serde(rename_all = "snake_case")]
pub enum StateKind {
Atomic,
Compound,
Parallel,
Final,
History(HistoryKind),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[serde(rename_all = "snake_case")]
pub enum HistoryKind {
Shallow,
Deep,
}
impl State {
pub fn atomic(id: impl Into<CompactString>) -> Self {
Self {
id: id.into(),
kind: StateKind::Atomic,
transitions: Vec::new(),
on_entry: Vec::new(),
on_exit: Vec::new(),
children: Vec::new(),
initial: None,
}
}
pub fn compound(
id: impl Into<CompactString>,
initial: impl Into<CompactString>,
children: Vec<State>,
) -> Self {
Self {
id: id.into(),
kind: StateKind::Compound,
transitions: Vec::new(),
on_entry: Vec::new(),
on_exit: Vec::new(),
children,
initial: Some(initial.into()),
}
}
pub fn parallel(id: impl Into<CompactString>, children: Vec<State>) -> Self {
Self {
id: id.into(),
kind: StateKind::Parallel,
transitions: Vec::new(),
on_entry: Vec::new(),
on_exit: Vec::new(),
children,
initial: None,
}
}
pub fn final_state(id: impl Into<CompactString>) -> Self {
Self {
id: id.into(),
kind: StateKind::Final,
transitions: Vec::new(),
on_entry: Vec::new(),
on_exit: Vec::new(),
children: Vec::new(),
initial: None,
}
}
pub fn history(id: impl Into<CompactString>, kind: HistoryKind) -> Self {
Self {
id: id.into(),
kind: StateKind::History(kind),
transitions: Vec::new(),
on_entry: Vec::new(),
on_exit: Vec::new(),
children: Vec::new(),
initial: None,
}
}
pub fn is_leaf(&self) -> bool {
matches!(self.kind, StateKind::Atomic | StateKind::Final)
}
pub fn is_composite(&self) -> bool {
matches!(self.kind, StateKind::Compound | StateKind::Parallel)
}
pub fn iter_all(&self) -> impl Iterator<Item = &State> {
StateIter { stack: vec![self] }
}
}
struct StateIter<'a> {
stack: Vec<&'a State>,
}
impl<'a> Iterator for StateIter<'a> {
type Item = &'a State;
fn next(&mut self) -> Option<Self::Item> {
let state = self.stack.pop()?;
for child in state.children.iter().rev() {
self.stack.push(child);
}
Some(state)
}
}