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