rs_statemachine/
lib.rs

1//! A flexible state machine implementation with optional advanced features
2//!
3//! # Features
4//!
5//! - `history` - State transition history tracking
6//! - `extended` - Entry/exit actions for states
7//! - `metrics` - Performance metrics collection
8//! - `hierarchical` - Hierarchical state support
9//! - `guards` - Guard conditions with priorities
10//! - `timeout` - State timeout support
11//! - `parallel` - Parallel state regions
12//! - `visualization` - Export to DOT/PlantUML
13//! - `serde` - Serialization support
14//! - `async` - Async action support
15//!
16//! # How to use rs-statemachine
17//!
18//!```rust
19//! use rs_statemachine::*;
20//! // Define your states
21//! #[derive(Debug, Clone, Hash, Eq, PartialEq)]
22//! enum MyState {
23//!     Idle,
24//!     Working,
25//!     Done,
26//! }
27//! impl State for MyState {}
28//!
29//! // Define your events
30//! #[derive(Debug, Clone, Hash, Eq, PartialEq)]
31//! enum MyEvent {
32//!     Start,
33//!     Complete,
34//! }
35//! impl Event for MyEvent {}
36//!
37//! // Define your context
38//! #[derive(Debug, Clone)]
39//! struct MyContext {
40//!     task_id: String,
41//! }
42//! impl Context for MyContext {}
43//!
44//! // Build the state machine
45//! let mut builder = StateMachineBuilderFactory::create::<MyState, MyEvent, MyContext>();
46//! builder
47//!     .external_transition()
48//!     .from(MyState::Idle)
49//!     .to(MyState::Working)
50//!     .on(MyEvent::Start)
51//!     .perform(|_s, _e, ctx| {
52//!         println!("Starting task {}", ctx.task_id);
53//!     });
54//! let state_machine = builder.build();
55//!
56//! let context = MyContext {
57//!             task_id: "frank".to_string(),
58//!         };
59//! state_machine.fire_event(MyState::Idle, MyEvent::Start, context);
60//! ```
61//!
62
63use 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
73/// Trait for state machine states
74pub 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
84/// Trait for state machine events
85pub 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
95/// Trait for state machine context
96pub 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
106/// Type alias for condition functions
107pub type Condition<S, E, C> = Arc<dyn Fn(&S, &E, &C) -> bool + Send + Sync>;
108
109/// Type alias for action functions
110pub type Action<S, E, C> = Arc<dyn Fn(&S, &E, &C) -> () + Send + Sync>;
111
112/// Type alias for fail callback functions
113pub type FailCallback<S, E, C> = Arc<dyn Fn(&S, &E, &C) + Send + Sync>;
114
115/// Represents a transition in the state machine
116#[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/// Type of transition
134#[derive(Debug, Clone, PartialEq)]
135pub enum TransitionType {
136    External,
137    Internal,
138}
139
140/// Error types for state machine operations
141#[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// History tracking feature
176#[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// Metrics feature
191#[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// Extended state machine features
232#[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// Hierarchical state support
245#[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// Async support
253#[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
267/// The main state machine struct
268pub 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    /// Fire an event and perform state transition
303    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            // Execute exit action for current state
310            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                // Sort by priority if guards feature is enabled
324                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                // Execute action if present
336                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            // Execute entry action for new state
366            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    /// Verify if a transition is possible
423    pub fn verify(&self, from: S, event: E) -> bool {
424        let key = (from, event);
425        self.transitions.contains_key(&key)
426    }
427
428    /// Get the ID of the state machine
429    pub fn id(&self) -> &str {
430        &self.id
431    }
432
433    #[cfg(feature = "history")]
434    /// Get transition history
435    pub fn get_history(&self) -> Vec<TransitionRecord<S, E>> {
436        self.history.lock().unwrap().clone()
437    }
438
439    #[cfg(feature = "history")]
440    /// Clear transition history
441    pub fn clear_history(&self) {
442        self.history.lock().unwrap().clear();
443    }
444
445    #[cfg(feature = "metrics")]
446    /// Get metrics
447    pub fn get_metrics(&self) -> StateMachineMetrics {
448        self.metrics.lock().unwrap().clone()
449    }
450
451    #[cfg(feature = "extended")]
452    /// Add entry action for a state
453    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    /// Add exit action for a state
467    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    /// Set timeout for a state
481    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    /// Export to DOT format
495    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    /// Export to PlantUML format
515    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    /// Fire an event asynchronously
540    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
556/// Builder for creating state machines with fluent API
557pub 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    /// Create a new state machine builder
583    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    /// Set the ID of the state machine
600    pub fn id(mut self, id: impl Into<String>) -> Self {
601        self.id = Some(id.into());
602        self
603    }
604
605    /// Start building an external transition
606    pub fn external_transition(&mut self) -> ExternalTransitionBuilder<S, E, C> {
607        ExternalTransitionBuilder::new(self)
608    }
609
610    /// Start building an internal transition
611    pub fn internal_transition(&mut self) -> InternalTransitionBuilder<S, E, C> {
612        InternalTransitionBuilder::new(self)
613    }
614
615    /// Start building external transitions from multiple states
616    pub fn external_transitions(&mut self) -> ExternalTransitionsBuilder<S, E, C> {
617        ExternalTransitionsBuilder::new(self)
618    }
619
620    /// Set fail callback
621    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    /// Add entry action for a state
628    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    /// Add exit action for a state
643    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    /// Set timeout for a state
658    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    /// Build the state machine
672    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
719/// Builder for external transitions
720pub 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
809/// Builder for internal transitions
810pub 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
893/// Builder for external transitions from multiple states
894pub 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
991/// Factory for creating state machine builders
992pub 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
1005/// Factory for managing multiple state machines
1006pub 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// Parallel state machine support (requires parallel feature)
1060#[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); // Should fail
1242
1243        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}