1pub mod error;
7pub mod machine;
8pub mod macros;
9pub mod transition;
10
11pub use error::BuildError;
12pub use machine::StateMachineBuilder;
13pub use transition::TransitionBuilder;
14
15use crate::core::State;
16use crate::effects::{Transition, TransitionResult};
17use stillwater::prelude::*;
18
19pub fn simple_transition<S, Env>(from: S, to: S) -> Transition<S, Env>
38where
39 S: State + 'static,
40 Env: Clone + Send + Sync + 'static,
41{
42 let to_clone = to.clone();
43 TransitionBuilder::new()
44 .from(from)
45 .to(to)
46 .action(move || pure(TransitionResult::Success(to_clone.clone())).boxed())
47 .build()
48 .expect("Simple transition should always build")
49}
50
51pub fn guarded_transition<S, Env, F>(from: S, to: S, guard: F) -> Transition<S, Env>
76where
77 S: State + 'static,
78 Env: Clone + Send + Sync + 'static,
79 F: Fn(&S) -> bool + Send + Sync + 'static,
80{
81 let to_clone = to.clone();
82 TransitionBuilder::new()
83 .from(from)
84 .to(to)
85 .when(guard)
86 .action(move || pure(TransitionResult::Success(to_clone.clone())).boxed())
87 .build()
88 .expect("Guarded transition should always build")
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use serde::{Deserialize, Serialize};
95
96 #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
97 enum TestState {
98 Start,
99 Middle,
100 End,
101 }
102
103 impl State for TestState {
104 fn name(&self) -> &str {
105 match self {
106 Self::Start => "Start",
107 Self::Middle => "Middle",
108 Self::End => "End",
109 }
110 }
111
112 fn is_final(&self) -> bool {
113 matches!(self, Self::End)
114 }
115 }
116
117 #[test]
118 fn simple_transition_builds() {
119 let transition = simple_transition::<TestState, ()>(TestState::Start, TestState::Middle);
120
121 assert_eq!(transition.from, TestState::Start);
122 assert_eq!(transition.to, TestState::Middle);
123 assert!(transition.can_execute(&TestState::Start));
124 }
125
126 #[test]
127 fn guarded_transition_respects_guard() {
128 let transition =
129 guarded_transition::<TestState, (), _>(TestState::Start, TestState::Middle, |s| {
130 !s.is_final()
131 });
132
133 assert!(transition.can_execute(&TestState::Start));
134 assert!(!transition.can_execute(&TestState::End));
135 }
136}