Skip to main content

qubit_state_machine/
state_machine_builder.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Builder for immutable state machine rules.
10
11use std::collections::{HashMap, HashSet};
12use std::fmt::Debug;
13use std::hash::Hash;
14
15use crate::{StateMachine, StateMachineBuildError, Transition};
16
17/// Builder used to define and validate finite state machine rules.
18///
19/// `S` is the state type and `E` is the event type. Configuration methods
20/// consume and return the builder so rule definitions can be chained. The built
21/// [`StateMachine`] is immutable.
22#[derive(Debug, Clone)]
23pub struct StateMachineBuilder<S, E>
24where
25    S: Copy + Eq + Hash + Debug,
26    E: Copy + Eq + Hash + Debug,
27{
28    pub(crate) states: HashSet<S>,
29    pub(crate) initial_states: HashSet<S>,
30    pub(crate) final_states: HashSet<S>,
31    pub(crate) transitions: Vec<Transition<S, E>>,
32}
33
34impl<S, E> StateMachineBuilder<S, E>
35where
36    S: Copy + Eq + Hash + Debug + 'static,
37    E: Copy + Eq + Hash + Debug + 'static,
38{
39    /// Creates an empty state machine builder.
40    ///
41    /// # Returns
42    /// A builder with no states or transitions.
43    pub fn new() -> Self {
44        Self {
45            states: HashSet::new(),
46            initial_states: HashSet::new(),
47            final_states: HashSet::new(),
48            transitions: Vec::new(),
49        }
50    }
51
52    /// Adds a state to the state machine definition.
53    ///
54    /// # Parameters
55    /// - `state`: State to register.
56    ///
57    /// # Returns
58    /// The updated builder.
59    pub fn add_state(mut self, state: S) -> Self {
60        self.states.insert(state);
61        self
62    }
63
64    /// Adds multiple states to the state machine definition.
65    ///
66    /// # Parameters
67    /// - `states`: States to register.
68    ///
69    /// # Returns
70    /// The updated builder.
71    pub fn add_states(mut self, states: &[S]) -> Self {
72        self.states.extend(states.iter().copied());
73        self
74    }
75
76    /// Marks a state as an initial state.
77    ///
78    /// The state must also be registered through [`add_state`](Self::add_state)
79    /// or [`add_states`](Self::add_states) before [`build`](Self::build) is
80    /// called.
81    ///
82    /// # Parameters
83    /// - `state`: Initial state to add.
84    ///
85    /// # Returns
86    /// The updated builder.
87    pub fn set_initial_state(mut self, state: S) -> Self {
88        self.initial_states.insert(state);
89        self
90    }
91
92    /// Marks multiple states as initial states.
93    ///
94    /// # Parameters
95    /// - `states`: Initial states to add.
96    ///
97    /// # Returns
98    /// The updated builder.
99    pub fn set_initial_states(mut self, states: &[S]) -> Self {
100        self.initial_states.extend(states.iter().copied());
101        self
102    }
103
104    /// Marks a state as a final state.
105    ///
106    /// The state must also be registered through [`add_state`](Self::add_state)
107    /// or [`add_states`](Self::add_states) before [`build`](Self::build) is
108    /// called.
109    ///
110    /// # Parameters
111    /// - `state`: Final state to add.
112    ///
113    /// # Returns
114    /// The updated builder.
115    pub fn set_final_state(mut self, state: S) -> Self {
116        self.final_states.insert(state);
117        self
118    }
119
120    /// Marks multiple states as final states.
121    ///
122    /// # Parameters
123    /// - `states`: Final states to add.
124    ///
125    /// # Returns
126    /// The updated builder.
127    pub fn set_final_states(mut self, states: &[S]) -> Self {
128        self.final_states.extend(states.iter().copied());
129        self
130    }
131
132    /// Adds a transition by source state, event, and target state.
133    ///
134    /// Source and target states must be registered before
135    /// [`build`](Self::build) is called. Adding the same transition more than
136    /// once is allowed. Adding the same `(source, event)` with a different
137    /// target is rejected during build.
138    ///
139    /// # Parameters
140    /// - `source`: State before the event is applied.
141    /// - `event`: Event that triggers the transition.
142    /// - `target`: State after the transition succeeds.
143    ///
144    /// # Returns
145    /// The updated builder.
146    pub fn add_transition(self, source: S, event: E, target: S) -> Self {
147        self.add_transition_value(Transition::new(source, event, target))
148    }
149
150    /// Adds a transition value.
151    ///
152    /// # Parameters
153    /// - `transition`: Transition to add to the state machine definition.
154    ///
155    /// # Returns
156    /// The updated builder.
157    pub fn add_transition_value(mut self, transition: Transition<S, E>) -> Self {
158        self.transitions.push(transition);
159        self
160    }
161
162    /// Builds an immutable state machine after validating the rule set.
163    ///
164    /// # Returns
165    /// A validated immutable state machine.
166    ///
167    /// # Errors
168    /// Returns a [`StateMachineBuildError`] when an initial state, final state,
169    /// transition source, or transition target is not registered, or when two
170    /// transitions map the same `(source, event)` pair to different targets.
171    pub fn build(self) -> Result<StateMachine<S, E>, StateMachineBuildError<S, E>> {
172        self.validate_registered_states()?;
173
174        let mut transition_set = HashSet::new();
175        let mut transition_map = HashMap::new();
176        for transition in &self.transitions {
177            let transition = *transition;
178            self.validate_transition(transition)?;
179            Self::insert_transition(transition, &mut transition_set, &mut transition_map)?;
180        }
181
182        Ok(StateMachine::new(self, transition_set, transition_map))
183    }
184
185    /// Validates that initial and final states are registered.
186    ///
187    /// # Returns
188    /// `Ok(())` when all configured state sets refer to registered states.
189    ///
190    /// # Errors
191    /// Returns the first unregistered initial or final state encountered.
192    fn validate_registered_states(&self) -> Result<(), StateMachineBuildError<S, E>> {
193        for state in &self.initial_states {
194            if !self.states.contains(state) {
195                return Err(StateMachineBuildError::InitialStateNotRegistered { state: *state });
196            }
197        }
198        for state in &self.final_states {
199            if !self.states.contains(state) {
200                return Err(StateMachineBuildError::FinalStateNotRegistered { state: *state });
201            }
202        }
203        Ok(())
204    }
205
206    /// Validates that a transition only references registered states.
207    ///
208    /// # Parameters
209    /// - `transition`: Transition to validate.
210    ///
211    /// # Returns
212    /// `Ok(())` when the transition source and target are registered.
213    ///
214    /// # Errors
215    /// Returns the missing source or target as a build error.
216    fn validate_transition(
217        &self,
218        transition: Transition<S, E>,
219    ) -> Result<(), StateMachineBuildError<S, E>> {
220        if !self.states.contains(&transition.source()) {
221            return Err(StateMachineBuildError::TransitionSourceNotRegistered {
222                source: transition.source(),
223                event: transition.event(),
224                target: transition.target(),
225            });
226        }
227        if !self.states.contains(&transition.target()) {
228            return Err(StateMachineBuildError::TransitionTargetNotRegistered {
229                source: transition.source(),
230                event: transition.event(),
231                target: transition.target(),
232            });
233        }
234        Ok(())
235    }
236
237    /// Inserts a transition into the set and lookup table.
238    ///
239    /// # Parameters
240    /// - `transition`: Transition to insert.
241    /// - `transition_set`: Set used for public transition inspection.
242    /// - `transition_map`: Lookup table used for event triggering.
243    ///
244    /// # Returns
245    /// `Ok(())` when the transition is inserted or is an exact duplicate.
246    ///
247    /// # Errors
248    /// Returns a duplicate-transition error if the same source and event already
249    /// point to a different target.
250    fn insert_transition(
251        transition: Transition<S, E>,
252        transition_set: &mut HashSet<Transition<S, E>>,
253        transition_map: &mut HashMap<(S, E), S>,
254    ) -> Result<(), StateMachineBuildError<S, E>> {
255        let source = transition.source();
256        let event = transition.event();
257        let target = transition.target();
258        if let Some(existing_target) = transition_map.get(&(source, event))
259            && *existing_target != target
260        {
261            return Err(StateMachineBuildError::DuplicateTransition {
262                source,
263                event,
264                existing_target: *existing_target,
265                new_target: target,
266            });
267        }
268        transition_set.insert(transition);
269        transition_map.insert((source, event), target);
270        Ok(())
271    }
272}
273
274impl<S, E> Default for StateMachineBuilder<S, E>
275where
276    S: Copy + Eq + Hash + Debug + 'static,
277    E: Copy + Eq + Hash + Debug + 'static,
278{
279    /// Creates an empty state machine builder.
280    fn default() -> Self {
281        Self::new()
282    }
283}