1use std::collections::HashMap;
64use std::fmt::Debug;
65use std::hash::Hash;
66use std::sync::Arc;
67
68#[cfg(feature = "history")]
69use std::sync::Mutex;
70#[cfg(any(feature = "history", feature = "timeout", feature = "metrics"))]
71use std::time::{Duration, Instant};
72
73pub trait State: Debug + Clone + Hash + Eq + PartialEq {
75 #[cfg(feature = "serde")]
76 fn serialize(&self) -> Result<String, Box<dyn std::error::Error>>
77 where
78 Self: serde::Serialize,
79 {
80 serde_json::to_string(self).map_err(|e| e.into())
81 }
82}
83
84pub trait Event: Debug + Clone + Hash + Eq + PartialEq {
86 #[cfg(feature = "serde")]
87 fn serialize(&self) -> Result<String, Box<dyn std::error::Error>>
88 where
89 Self: serde::Serialize,
90 {
91 serde_json::to_string(self).map_err(|e| e.into())
92 }
93}
94
95pub trait Context: Debug + Clone {
97 #[cfg(feature = "serde")]
98 fn serialize(&self) -> Result<String, Box<dyn std::error::Error>>
99 where
100 Self: serde::Serialize,
101 {
102 serde_json::to_string(self).map_err(|e| e.into())
103 }
104}
105
106pub type Condition<S, E, C> = Arc<dyn Fn(&S, &E, &C) -> bool + Send + Sync>;
108
109pub type Action<S, E, C> = Arc<dyn Fn(&S, &E, &C) -> () + Send + Sync>;
111
112pub type FailCallback<S, E, C> = Arc<dyn Fn(&S, &E, &C) + Send + Sync>;
114
115#[derive(Clone)]
117pub struct Transition<S, E, C>
118where
119 S: State,
120 E: Event,
121 C: Context,
122{
123 from: S,
124 to: S,
125 event: E,
126 condition: Option<Condition<S, E, C>>,
127 action: Option<Action<S, E, C>>,
128 transition_type: TransitionType,
129 #[cfg(feature = "guards")]
130 priority: u32,
131}
132
133#[derive(Debug, Clone, PartialEq)]
135pub enum TransitionType {
136 External,
137 Internal,
138}
139
140#[derive(Debug, Clone)]
142pub enum TransitionError {
143 NoValidTransition {
144 from: String,
145 event: String,
146 },
147 ConditionFailed,
148 #[cfg(feature = "timeout")]
149 Timeout,
150 #[cfg(feature = "async")]
151 AsyncError(String),
152}
153
154impl std::fmt::Display for TransitionError {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 TransitionError::NoValidTransition { from, event } => {
158 write!(
159 f,
160 "No valid transition from state {} with event {}",
161 from, event
162 )
163 }
164 TransitionError::ConditionFailed => write!(f, "Transition condition failed"),
165 #[cfg(feature = "timeout")]
166 TransitionError::Timeout => write!(f, "State timeout occurred"),
167 #[cfg(feature = "async")]
168 TransitionError::AsyncError(msg) => write!(f, "Async error: {}", msg),
169 }
170 }
171}
172
173impl std::error::Error for TransitionError {}
174
175#[cfg(feature = "history")]
177#[derive(Debug, Clone)]
178pub struct TransitionRecord<S, E>
179where
180 S: State,
181 E: Event,
182{
183 pub from: S,
184 pub to: S,
185 pub event: E,
186 pub timestamp: Instant,
187 pub success: bool,
188}
189
190#[cfg(feature = "metrics")]
192#[derive(Debug, Clone)]
193pub struct StateMachineMetrics {
194 pub total_transitions: u64,
195 pub successful_transitions: u64,
196 pub failed_transitions: u64,
197 pub transition_durations: Vec<Duration>,
198 pub state_visit_counts: HashMap<String, u64>,
199}
200
201#[cfg(feature = "metrics")]
202impl StateMachineMetrics {
203 pub fn new() -> Self {
204 StateMachineMetrics {
205 total_transitions: 0,
206 successful_transitions: 0,
207 failed_transitions: 0,
208 transition_durations: Vec::new(),
209 state_visit_counts: HashMap::new(),
210 }
211 }
212
213 pub fn average_transition_time(&self) -> Option<Duration> {
214 if self.transition_durations.is_empty() {
215 None
216 } else {
217 let total: Duration = self.transition_durations.iter().sum();
218 Some(total / self.transition_durations.len() as u32)
219 }
220 }
221
222 pub fn success_rate(&self) -> f64 {
223 if self.total_transitions == 0 {
224 0.0
225 } else {
226 self.successful_transitions as f64 / self.total_transitions as f64
227 }
228 }
229}
230
231#[cfg(feature = "extended")]
233pub struct StateActions<S, E, C>
234where
235 S: State,
236 E: Event,
237 C: Context,
238{
239 pub on_entry: Option<Arc<dyn Fn(&S, &C) + Send + Sync>>,
240 pub on_exit: Option<Arc<dyn Fn(&S, &C) + Send + Sync>>,
241 _phantom: std::marker::PhantomData<E>,
242}
243
244#[cfg(feature = "hierarchical")]
246pub trait HierarchicalState: State {
247 fn parent(&self) -> Option<Self>;
248 fn children(&self) -> Vec<Self>;
249 fn is_substate_of(&self, other: &Self) -> bool;
250}
251
252#[cfg(feature = "async")]
254use async_trait::async_trait;
255
256#[cfg(feature = "async")]
257#[async_trait]
258pub trait AsyncAction<S, E, C>: Send + Sync
259where
260 S: State + Send,
261 E: Event + Send,
262 C: Context + Send,
263{
264 async fn execute(&self, from: &S, event: &E, context: &C);
265}
266
267pub struct StateMachine<S, E, C>
269where
270 S: State,
271 E: Event,
272 C: Context,
273{
274 id: String,
275 transitions: HashMap<(S, E), Vec<Transition<S, E, C>>>,
276 fail_callback: Option<FailCallback<S, E, C>>,
277
278 #[cfg(feature = "history")]
279 history: Arc<Mutex<Vec<TransitionRecord<S, E>>>>,
280
281 #[cfg(feature = "metrics")]
282 metrics: Arc<Mutex<StateMachineMetrics>>,
283
284 #[cfg(feature = "extended")]
285 state_actions: HashMap<S, StateActions<S, E, C>>,
286
287 #[cfg(feature = "timeout")]
288 state_timeouts: HashMap<S, Duration>,
289 #[cfg(feature = "timeout")]
290 timeout_transitions: HashMap<S, (S, E)>,
291
292 #[cfg(feature = "async")]
293 async_actions: HashMap<(S, E), Box<dyn AsyncAction<S, E, C>>>,
294}
295
296impl<S, E, C> StateMachine<S, E, C>
297where
298 S: State,
299 E: Event,
300 C: Context,
301{
302 pub fn fire_event(&self, from: S, event: E, context: C) -> Result<S, TransitionError> {
304 #[cfg(feature = "metrics")]
305 let start_time = Instant::now();
306
307 #[cfg(feature = "extended")]
308 {
309 if let Some(actions) = self.state_actions.get(&from) {
311 if let Some(on_exit) = &actions.on_exit {
312 on_exit(&from, &context);
313 }
314 }
315 }
316
317 let key = (from.clone(), event.clone());
318 let result = if let Some(transitions) = self.transitions.get(&key) {
319 let mut valid_transitions = transitions.clone();
320
321 #[cfg(feature = "guards")]
322 {
323 valid_transitions.sort_by_key(|t| std::cmp::Reverse(t.priority));
325 }
326
327 let mut transition_result = None;
328 for transition in valid_transitions {
329 if let Some(condition) = &transition.condition {
330 if !condition(&from, &event, &context) {
331 continue;
332 }
333 }
334
335 if let Some(action) = &transition.action {
337 action(&from, &event, &context);
338 }
339
340 transition_result = Some(Ok(transition.to.clone()));
341 break;
342 }
343
344 transition_result.unwrap_or_else(|| {
345 if let Some(fail_callback) = &self.fail_callback {
346 fail_callback(&from, &event, &context);
347 }
348 Err(TransitionError::NoValidTransition {
349 from: format!("{:?}", from),
350 event: format!("{:?}", event),
351 })
352 })
353 } else {
354 if let Some(fail_callback) = &self.fail_callback {
355 fail_callback(&from, &event, &context);
356 }
357 Err(TransitionError::NoValidTransition {
358 from: format!("{:?}", from),
359 event: format!("{:?}", event),
360 })
361 };
362
363 #[cfg(feature = "extended")]
364 {
365 if let Ok(new_state) = &result {
367 if let Some(actions) = self.state_actions.get(new_state) {
368 if let Some(on_entry) = &actions.on_entry {
369 on_entry(new_state, &context);
370 }
371 }
372 }
373 }
374
375 #[cfg(feature = "history")]
376 {
377 let record = match &result {
378 Ok(to_state) => TransitionRecord {
379 from: from.clone(),
380 to: to_state.clone(),
381 event: event.clone(),
382 timestamp: Instant::now(),
383 success: true,
384 },
385 Err(_) => TransitionRecord {
386 from: from.clone(),
387 to: from.clone(),
388 event: event.clone(),
389 timestamp: Instant::now(),
390 success: false,
391 },
392 };
393
394 if let Ok(mut history) = self.history.lock() {
395 history.push(record);
396 }
397 }
398
399 #[cfg(feature = "metrics")]
400 {
401 let duration = start_time.elapsed();
402 if let Ok(mut metrics) = self.metrics.lock() {
403 metrics.total_transitions += 1;
404 metrics.transition_durations.push(duration);
405
406 match &result {
407 Ok(to_state) => {
408 metrics.successful_transitions += 1;
409 let state_name = format!("{:?}", to_state);
410 *metrics.state_visit_counts.entry(state_name).or_insert(0) += 1;
411 }
412 Err(_) => {
413 metrics.failed_transitions += 1;
414 }
415 }
416 }
417 }
418
419 result
420 }
421
422 pub fn verify(&self, from: S, event: E) -> bool {
424 let key = (from, event);
425 self.transitions.contains_key(&key)
426 }
427
428 pub fn id(&self) -> &str {
430 &self.id
431 }
432
433 #[cfg(feature = "history")]
434 pub fn get_history(&self) -> Vec<TransitionRecord<S, E>> {
436 self.history.lock().unwrap().clone()
437 }
438
439 #[cfg(feature = "history")]
440 pub fn clear_history(&self) {
442 self.history.lock().unwrap().clear();
443 }
444
445 #[cfg(feature = "metrics")]
446 pub fn get_metrics(&self) -> StateMachineMetrics {
448 self.metrics.lock().unwrap().clone()
449 }
450
451 #[cfg(feature = "extended")]
452 pub fn add_entry_action<F>(&mut self, state: S, action: F)
454 where
455 F: Fn(&S, &C) + Send + Sync + 'static,
456 {
457 let actions = self.state_actions.entry(state).or_insert(StateActions {
458 on_entry: None,
459 on_exit: None,
460 _phantom: Default::default(),
461 });
462 actions.on_entry = Some(Arc::new(action));
463 }
464
465 #[cfg(feature = "extended")]
466 pub fn add_exit_action<F>(&mut self, state: S, action: F)
468 where
469 F: Fn(&S, &C) + Send + Sync + 'static,
470 {
471 let actions = self.state_actions.entry(state).or_insert(StateActions {
472 on_entry: None,
473 on_exit: None,
474 _phantom: Default::default(),
475 });
476 actions.on_exit = Some(Arc::new(action));
477 }
478
479 #[cfg(feature = "timeout")]
480 pub fn set_state_timeout(
482 &mut self,
483 state: S,
484 duration: Duration,
485 target_state: S,
486 timeout_event: E,
487 ) {
488 self.state_timeouts.insert(state.clone(), duration);
489 self.timeout_transitions
490 .insert(state, (target_state, timeout_event));
491 }
492
493 #[cfg(feature = "visualization")]
494 pub fn to_dot(&self) -> String {
496 let mut dot = String::from("digraph StateMachine {\n");
497 dot.push_str(" rankdir=LR;\n");
498 dot.push_str(" node [shape=box];\n\n");
499
500 for ((from, event), transitions) in &self.transitions {
501 for transition in transitions {
502 dot.push_str(&format!(
503 " \"{:?}\" -> \"{:?}\" [label=\"{:?}\"];\n",
504 from, transition.to, event
505 ));
506 }
507 }
508
509 dot.push_str("}\n");
510 dot
511 }
512
513 #[cfg(feature = "visualization")]
514 pub fn to_plantuml(&self) -> String {
516 let mut uml = String::from("@startuml\n");
517
518 for ((from, event), transitions) in &self.transitions {
519 for transition in transitions {
520 uml.push_str(&format!(
521 "{:?} --> {:?} : {:?}\n",
522 from, transition.to, event
523 ));
524 }
525 }
526
527 uml.push_str("@enduml\n");
528 uml
529 }
530}
531
532#[cfg(feature = "async")]
533impl<S, E, C> StateMachine<S, E, C>
534where
535 S: State + Send + Sync,
536 E: Event + Send + Sync,
537 C: Context + Send + Sync,
538{
539 pub async fn fire_event_async(
541 &self,
542 from: S,
543 event: E,
544 context: C,
545 ) -> Result<S, TransitionError> {
546 let key = (from.clone(), event.clone());
547
548 if let Some(async_action) = self.async_actions.get(&key) {
549 async_action.execute(&from, &event, &context).await;
550 }
551
552 self.fire_event(from, event, context)
553 }
554}
555
556pub struct StateMachineBuilder<S, E, C>
558where
559 S: State,
560 E: Event,
561 C: Context,
562{
563 id: Option<String>,
564 transitions: Vec<Transition<S, E, C>>,
565 fail_callback: Option<FailCallback<S, E, C>>,
566 #[cfg(feature = "extended")]
567 state_actions: HashMap<S, StateActions<S, E, C>>,
568 #[cfg(feature = "timeout")]
569 state_timeouts: HashMap<S, Duration>,
570 #[cfg(feature = "timeout")]
571 timeout_transitions: HashMap<S, (S, E)>,
572 #[cfg(feature = "async")]
573 async_actions: HashMap<(S, E), Box<dyn AsyncAction<S, E, C>>>,
574}
575
576impl<S, E, C> StateMachineBuilder<S, E, C>
577where
578 S: State,
579 E: Event,
580 C: Context,
581{
582 pub fn new() -> Self {
584 StateMachineBuilder {
585 id: None,
586 transitions: Vec::new(),
587 fail_callback: None,
588 #[cfg(feature = "extended")]
589 state_actions: HashMap::new(),
590 #[cfg(feature = "timeout")]
591 state_timeouts: HashMap::new(),
592 #[cfg(feature = "timeout")]
593 timeout_transitions: HashMap::new(),
594 #[cfg(feature = "async")]
595 async_actions: HashMap::new(),
596 }
597 }
598
599 pub fn id(mut self, id: impl Into<String>) -> Self {
601 self.id = Some(id.into());
602 self
603 }
604
605 pub fn external_transition(&mut self) -> ExternalTransitionBuilder<S, E, C> {
607 ExternalTransitionBuilder::new(self)
608 }
609
610 pub fn internal_transition(&mut self) -> InternalTransitionBuilder<S, E, C> {
612 InternalTransitionBuilder::new(self)
613 }
614
615 pub fn external_transitions(&mut self) -> ExternalTransitionsBuilder<S, E, C> {
617 ExternalTransitionsBuilder::new(self)
618 }
619
620 pub fn set_fail_callback(&mut self, callback: FailCallback<S, E, C>) -> &mut Self {
622 self.fail_callback = Some(callback);
623 self
624 }
625
626 #[cfg(feature = "extended")]
627 pub fn with_entry_action<F>(&mut self, state: S, action: F) -> &mut Self
629 where
630 F: Fn(&S, &C) + Send + Sync + 'static,
631 {
632 let actions = self.state_actions.entry(state).or_insert(StateActions {
633 on_entry: None,
634 on_exit: None,
635 _phantom: Default::default(),
636 });
637 actions.on_entry = Some(Arc::new(action));
638 self
639 }
640
641 #[cfg(feature = "extended")]
642 pub fn with_exit_action<F>(&mut self, state: S, action: F) -> &mut Self
644 where
645 F: Fn(&S, &C) + Send + Sync + 'static,
646 {
647 let actions = self.state_actions.entry(state).or_insert(StateActions {
648 on_entry: None,
649 on_exit: None,
650 _phantom: Default::default(),
651 });
652 actions.on_exit = Some(Arc::new(action));
653 self
654 }
655
656 #[cfg(feature = "timeout")]
657 pub fn with_state_timeout(
659 &mut self,
660 state: S,
661 duration: Duration,
662 target_state: S,
663 timeout_event: E,
664 ) -> &mut Self {
665 self.state_timeouts.insert(state.clone(), duration);
666 self.timeout_transitions
667 .insert(state, (target_state, timeout_event));
668 self
669 }
670
671 pub fn build(self) -> StateMachine<S, E, C> {
673 let id = self.id.unwrap_or_else(|| "StateMachine".to_string());
674 let mut transitions_map = HashMap::new();
675
676 for transition in self.transitions {
677 let key = (transition.from.clone(), transition.event.clone());
678 transitions_map
679 .entry(key)
680 .or_insert_with(Vec::new)
681 .push(transition);
682 }
683
684 StateMachine {
685 id,
686 transitions: transitions_map,
687 fail_callback: self.fail_callback,
688 #[cfg(feature = "history")]
689 history: Arc::new(Mutex::new(Vec::new())),
690 #[cfg(feature = "metrics")]
691 metrics: Arc::new(Mutex::new(StateMachineMetrics::new())),
692 #[cfg(feature = "extended")]
693 state_actions: self.state_actions,
694 #[cfg(feature = "timeout")]
695 state_timeouts: self.state_timeouts,
696 #[cfg(feature = "timeout")]
697 timeout_transitions: self.timeout_transitions,
698 #[cfg(feature = "async")]
699 async_actions: self.async_actions,
700 }
701 }
702
703 fn add_transition(&mut self, transition: Transition<S, E, C>) {
704 self.transitions.push(transition);
705 }
706}
707
708impl<S, E, C> Default for StateMachineBuilder<S, E, C>
709where
710 S: State,
711 E: Event,
712 C: Context,
713{
714 fn default() -> Self {
715 Self::new()
716 }
717}
718
719pub struct ExternalTransitionBuilder<'a, S, E, C>
721where
722 S: State,
723 E: Event,
724 C: Context,
725{
726 builder: &'a mut StateMachineBuilder<S, E, C>,
727 from: Option<S>,
728 to: Option<S>,
729 event: Option<E>,
730 condition: Option<Condition<S, E, C>>,
731 action: Option<Action<S, E, C>>,
732 #[cfg(feature = "guards")]
733 priority: u32,
734}
735
736impl<'a, S, E, C> ExternalTransitionBuilder<'a, S, E, C>
737where
738 S: State,
739 E: Event,
740 C: Context,
741{
742 fn new(builder: &'a mut StateMachineBuilder<S, E, C>) -> Self {
743 ExternalTransitionBuilder {
744 builder,
745 from: None,
746 to: None,
747 event: None,
748 condition: None,
749 action: None,
750 #[cfg(feature = "guards")]
751 priority: 0,
752 }
753 }
754
755 pub fn from(mut self, state: S) -> Self {
756 self.from = Some(state);
757 self
758 }
759
760 pub fn to(mut self, state: S) -> Self {
761 self.to = Some(state);
762 self
763 }
764
765 pub fn on(mut self, event: E) -> Self {
766 self.event = Some(event);
767 self
768 }
769
770 pub fn when<F>(mut self, condition: F) -> Self
771 where
772 F: Fn(&S, &E, &C) -> bool + Send + Sync + 'static,
773 {
774 self.condition = Some(Arc::new(condition));
775 self
776 }
777
778 #[cfg(feature = "guards")]
779 pub fn with_priority(mut self, priority: u32) -> Self {
780 self.priority = priority;
781 self
782 }
783
784 pub fn perform<F>(mut self, action: F) -> &'a mut StateMachineBuilder<S, E, C>
785 where
786 F: Fn(&S, &E, &C) -> () + Send + Sync + 'static,
787 {
788 self.action = Some(Arc::new(action));
789 self.build()
790 }
791
792 fn build(self) -> &'a mut StateMachineBuilder<S, E, C> {
793 let transition = Transition {
794 from: self.from.expect("from state is required"),
795 to: self.to.expect("to state is required"),
796 event: self.event.expect("event is required"),
797 condition: self.condition,
798 action: self.action,
799 transition_type: TransitionType::External,
800 #[cfg(feature = "guards")]
801 priority: self.priority,
802 };
803
804 self.builder.add_transition(transition);
805 self.builder
806 }
807}
808
809pub struct InternalTransitionBuilder<'a, S, E, C>
811where
812 S: State,
813 E: Event,
814 C: Context,
815{
816 builder: &'a mut StateMachineBuilder<S, E, C>,
817 within: Option<S>,
818 event: Option<E>,
819 condition: Option<Condition<S, E, C>>,
820 action: Option<Action<S, E, C>>,
821 #[cfg(feature = "guards")]
822 priority: u32,
823}
824
825impl<'a, S, E, C> InternalTransitionBuilder<'a, S, E, C>
826where
827 S: State,
828 E: Event,
829 C: Context,
830{
831 fn new(builder: &'a mut StateMachineBuilder<S, E, C>) -> Self {
832 InternalTransitionBuilder {
833 builder,
834 within: None,
835 event: None,
836 condition: None,
837 action: None,
838 #[cfg(feature = "guards")]
839 priority: 0,
840 }
841 }
842
843 pub fn within(mut self, state: S) -> Self {
844 self.within = Some(state);
845 self
846 }
847
848 pub fn on(mut self, event: E) -> Self {
849 self.event = Some(event);
850 self
851 }
852
853 pub fn when<F>(mut self, condition: F) -> Self
854 where
855 F: Fn(&S, &E, &C) -> bool + Send + Sync + 'static,
856 {
857 self.condition = Some(Arc::new(condition));
858 self
859 }
860
861 #[cfg(feature = "guards")]
862 pub fn with_priority(mut self, priority: u32) -> Self {
863 self.priority = priority;
864 self
865 }
866
867 pub fn perform<F>(mut self, action: F) -> &'a mut StateMachineBuilder<S, E, C>
868 where
869 F: Fn(&S, &E, &C) -> () + Send + Sync + 'static,
870 {
871 self.action = Some(Arc::new(action));
872 self.build()
873 }
874
875 fn build(self) -> &'a mut StateMachineBuilder<S, E, C> {
876 let state = self.within.expect("within state is required");
877 let transition = Transition {
878 from: state.clone(),
879 to: state,
880 event: self.event.expect("event is required"),
881 condition: self.condition,
882 action: self.action,
883 transition_type: TransitionType::Internal,
884 #[cfg(feature = "guards")]
885 priority: self.priority,
886 };
887
888 self.builder.add_transition(transition);
889 self.builder
890 }
891}
892
893pub struct ExternalTransitionsBuilder<'a, S, E, C>
895where
896 S: State,
897 E: Event,
898 C: Context,
899{
900 builder: &'a mut StateMachineBuilder<S, E, C>,
901 from_states: Vec<S>,
902 to: Option<S>,
903 event: Option<E>,
904 condition: Option<Condition<S, E, C>>,
905 action: Option<Action<S, E, C>>,
906 #[cfg(feature = "guards")]
907 priority: u32,
908}
909
910impl<'a, S, E, C> ExternalTransitionsBuilder<'a, S, E, C>
911where
912 S: State,
913 E: Event,
914 C: Context,
915{
916 fn new(builder: &'a mut StateMachineBuilder<S, E, C>) -> Self {
917 ExternalTransitionsBuilder {
918 builder,
919 from_states: Vec::new(),
920 to: None,
921 event: None,
922 condition: None,
923 action: None,
924 #[cfg(feature = "guards")]
925 priority: 0,
926 }
927 }
928
929 pub fn from_among(mut self, states: Vec<S>) -> Self {
930 self.from_states = states;
931 self
932 }
933
934 pub fn to(mut self, state: S) -> Self {
935 self.to = Some(state);
936 self
937 }
938
939 pub fn on(mut self, event: E) -> Self {
940 self.event = Some(event);
941 self
942 }
943
944 pub fn when<F>(mut self, condition: F) -> Self
945 where
946 F: Fn(&S, &E, &C) -> bool + Send + Sync + 'static,
947 {
948 self.condition = Some(Arc::new(condition));
949 self
950 }
951
952 #[cfg(feature = "guards")]
953 pub fn with_priority(mut self, priority: u32) -> Self {
954 self.priority = priority;
955 self
956 }
957
958 pub fn perform<F>(mut self, action: F) -> &'a mut StateMachineBuilder<S, E, C>
959 where
960 F: Fn(&S, &E, &C) -> () + Send + Sync + 'static,
961 {
962 self.action = Some(Arc::new(action));
963 self.build()
964 }
965
966 fn build(self) -> &'a mut StateMachineBuilder<S, E, C> {
967 let to = self.to.expect("to state is required");
968 let event = self.event.expect("event is required");
969 let condition = self.condition.clone();
970 let action = self.action.clone();
971
972 for from in self.from_states {
973 let transition = Transition {
974 from,
975 to: to.clone(),
976 event: event.clone(),
977 condition: condition.clone(),
978 action: action.clone(),
979 transition_type: TransitionType::External,
980 #[cfg(feature = "guards")]
981 priority: self.priority,
982 };
983
984 self.builder.add_transition(transition);
985 }
986
987 self.builder
988 }
989}
990
991pub struct StateMachineBuilderFactory;
993
994impl StateMachineBuilderFactory {
995 pub fn create<S, E, C>() -> StateMachineBuilder<S, E, C>
996 where
997 S: State,
998 E: Event,
999 C: Context,
1000 {
1001 StateMachineBuilder::new()
1002 }
1003}
1004
1005pub struct StateMachineFactory<S, E, C>
1007where
1008 S: State,
1009 E: Event,
1010 C: Context,
1011{
1012 machines: HashMap<String, StateMachine<S, E, C>>,
1013}
1014
1015impl<S, E, C> StateMachineFactory<S, E, C>
1016where
1017 S: State,
1018 E: Event,
1019 C: Context,
1020{
1021 pub fn new() -> Self {
1022 StateMachineFactory {
1023 machines: HashMap::new(),
1024 }
1025 }
1026
1027 pub fn register(&mut self, machine: StateMachine<S, E, C>) {
1028 self.machines.insert(machine.id.clone(), machine);
1029 }
1030
1031 pub fn get(&self, id: &str) -> Option<&StateMachine<S, E, C>> {
1032 self.machines.get(id)
1033 }
1034
1035 pub fn get_mut(&mut self, id: &str) -> Option<&mut StateMachine<S, E, C>> {
1036 self.machines.get_mut(id)
1037 }
1038
1039 pub fn remove(&mut self, id: &str) -> Option<StateMachine<S, E, C>> {
1040 self.machines.remove(id)
1041 }
1042
1043 pub fn list_ids(&self) -> Vec<&str> {
1044 self.machines.keys().map(|s| s.as_str()).collect()
1045 }
1046}
1047
1048impl<S, E, C> Default for StateMachineFactory<S, E, C>
1049where
1050 S: State,
1051 E: Event,
1052 C: Context,
1053{
1054 fn default() -> Self {
1055 Self::new()
1056 }
1057}
1058
1059#[cfg(feature = "parallel")]
1061pub struct ParallelStateMachine<S, E, C>
1062where
1063 S: State,
1064 E: Event,
1065 C: Context,
1066{
1067 regions: Vec<StateMachine<S, E, C>>,
1068}
1069
1070#[cfg(feature = "parallel")]
1071impl<S, E, C> ParallelStateMachine<S, E, C>
1072where
1073 S: State,
1074 E: Event,
1075 C: Context,
1076{
1077 pub fn new() -> Self {
1078 ParallelStateMachine {
1079 regions: Vec::new(),
1080 }
1081 }
1082
1083 pub fn add_region(&mut self, machine: StateMachine<S, E, C>) {
1084 self.regions.push(machine);
1085 }
1086
1087 pub fn fire_event(
1088 &self,
1089 states: Vec<S>,
1090 event: E,
1091 context: C,
1092 ) -> Vec<Result<S, TransitionError>> {
1093 self.regions
1094 .iter()
1095 .zip(states.iter())
1096 .map(|(machine, state)| {
1097 machine.fire_event(state.clone(), event.clone(), context.clone())
1098 })
1099 .collect()
1100 }
1101
1102 pub fn get_region(&self, index: usize) -> Option<&StateMachine<S, E, C>> {
1103 self.regions.get(index)
1104 }
1105
1106 pub fn region_count(&self) -> usize {
1107 self.regions.len()
1108 }
1109}
1110
1111#[cfg(test)]
1112mod tests {
1113 use super::*;
1114
1115 #[derive(Debug, Clone, Hash, Eq, PartialEq)]
1116 enum States {
1117 State1,
1118 State2,
1119 State3,
1120 State4,
1121 }
1122
1123 impl State for States {}
1124
1125 #[derive(Debug, Clone, Hash, Eq, PartialEq)]
1126 enum Events {
1127 Event1,
1128 Event2,
1129 Event3,
1130 Event4,
1131 InternalEvent,
1132 }
1133
1134 impl Event for Events {}
1135
1136 #[derive(Debug, Clone)]
1137 struct TestContext {
1138 operator: String,
1139 entity_id: String,
1140 }
1141
1142 impl Context for TestContext {}
1143
1144 #[test]
1145 fn test_basic_transition() {
1146 let mut builder = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1147
1148 builder
1149 .external_transition()
1150 .from(States::State1)
1151 .to(States::State2)
1152 .on(Events::Event1)
1153 .when(|_s, _e, c| c.operator == "frank")
1154 .perform(|_s, _e, c| {
1155 println!("Performing action for operator: {}", c.operator);
1156 });
1157
1158 let state_machine = builder.build();
1159
1160 let context = TestContext {
1161 operator: "frank".to_string(),
1162 entity_id: "123456".to_string(),
1163 };
1164
1165 let result = state_machine.fire_event(States::State1, Events::Event1, context);
1166 assert!(result.is_ok());
1167 assert_eq!(result.unwrap(), States::State2);
1168 }
1169
1170 #[test]
1171 #[cfg(feature = "history")]
1172 fn test_history_tracking() {
1173 let mut builder = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1174
1175 builder
1176 .external_transition()
1177 .from(States::State1)
1178 .to(States::State2)
1179 .on(Events::Event1)
1180 .perform(|_s, _e, _c| {});
1181
1182 let state_machine = builder.build();
1183 let context = TestContext {
1184 operator: "test".to_string(),
1185 entity_id: "789".to_string(),
1186 };
1187
1188 let _ = state_machine.fire_event(States::State1, Events::Event1, context);
1189 let history = state_machine.get_history();
1190 assert_eq!(history.len(), 1);
1191 assert!(history[0].success);
1192 }
1193
1194 #[test]
1195 #[cfg(feature = "extended")]
1196 fn test_entry_exit_actions() {
1197 let mut builder = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1198
1199 builder
1200 .with_entry_action(States::State2, |_s, _c| {
1201 println!("Entering State2");
1202 })
1203 .with_exit_action(States::State1, |_s, _c| {
1204 println!("Exiting State1");
1205 })
1206 .external_transition()
1207 .from(States::State1)
1208 .to(States::State2)
1209 .on(Events::Event1)
1210 .perform(|_s, _e, _c| {});
1211
1212 let state_machine = builder.build();
1213 let context = TestContext {
1214 operator: "test".to_string(),
1215 entity_id: "789".to_string(),
1216 };
1217
1218 let result = state_machine.fire_event(States::State1, Events::Event1, context);
1219 assert!(result.is_ok());
1220 }
1221
1222 #[test]
1223 #[cfg(feature = "metrics")]
1224 fn test_metrics_collection() {
1225 let mut builder = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1226
1227 builder
1228 .external_transition()
1229 .from(States::State1)
1230 .to(States::State2)
1231 .on(Events::Event1)
1232 .perform(|_s, _e, _c| {});
1233
1234 let state_machine = builder.build();
1235 let context = TestContext {
1236 operator: "test".to_string(),
1237 entity_id: "789".to_string(),
1238 };
1239
1240 let _ = state_machine.fire_event(States::State1, Events::Event1, context.clone());
1241 let _ = state_machine.fire_event(States::State1, Events::Event2, context); let metrics = state_machine.get_metrics();
1244 assert_eq!(metrics.total_transitions, 2);
1245 assert_eq!(metrics.successful_transitions, 1);
1246 assert_eq!(metrics.failed_transitions, 1);
1247 assert_eq!(metrics.success_rate(), 0.5);
1248 }
1249
1250 #[test]
1251 #[cfg(feature = "visualization")]
1252 fn test_visualization() {
1253 let mut builder = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1254
1255 builder
1256 .external_transition()
1257 .from(States::State1)
1258 .to(States::State2)
1259 .on(Events::Event1)
1260 .perform(|_s, _e, _c| {});
1261
1262 let state_machine = builder.build();
1263
1264 let dot = state_machine.to_dot();
1265 assert!(dot.contains("digraph StateMachine"));
1266 assert!(dot.contains("State1"));
1267 assert!(dot.contains("State2"));
1268
1269 let plantuml = state_machine.to_plantuml();
1270 assert!(plantuml.contains("@startuml"));
1271 assert!(plantuml.contains("State1"));
1272 assert!(plantuml.contains("State2"));
1273 }
1274
1275 #[test]
1276 #[cfg(feature = "parallel")]
1277 fn test_parallel_regions() {
1278 let mut builder1 = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1279 builder1
1280 .external_transition()
1281 .from(States::State1)
1282 .to(States::State2)
1283 .on(Events::Event1)
1284 .perform(|_s, _e, _c| {});
1285
1286 let mut builder2 = StateMachineBuilderFactory::create::<States, Events, TestContext>();
1287 builder2
1288 .external_transition()
1289 .from(States::State3)
1290 .to(States::State4)
1291 .on(Events::Event1)
1292 .perform(|_s, _e, _c| {});
1293
1294 let mut parallel_machine = ParallelStateMachine::new();
1295 parallel_machine.add_region(builder1.build());
1296 parallel_machine.add_region(builder2.build());
1297
1298 let context = TestContext {
1299 operator: "test".to_string(),
1300 entity_id: "789".to_string(),
1301 };
1302
1303 let results = parallel_machine.fire_event(
1304 vec![States::State1, States::State3],
1305 Events::Event1,
1306 context,
1307 );
1308
1309 assert_eq!(results.len(), 2);
1310 assert!(results[0].is_ok());
1311 assert!(results[1].is_ok());
1312 assert_eq!(results[0].as_ref().unwrap(), &States::State2);
1313 assert_eq!(results[1].as_ref().unwrap(), &States::State4);
1314 }
1315}