rustate/
machine.rs

1use crate::event::IntoEvent;
2use crate::{
3    action::ActionType, state::StateType, Action, Context, Error, Event, IntoAction, Result, State,
4    Transition,
5};
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet};
8
9/// Represents a state machine instance
10#[derive(Clone, Debug, Serialize, Deserialize)]
11pub struct Machine<S = State, E = Event>
12where
13    S: Clone + 'static + Default,
14    E: Clone + 'static,
15{
16    /// Name of the machine
17    pub name: String,
18    /// Collection of states
19    pub states: HashMap<String, State>,
20    /// Collection of transitions
21    pub transitions: Vec<Transition>,
22    /// Initial state id
23    pub initial: String,
24    /// Current state(s)
25    pub current_states: HashSet<String>,
26    /// Extended state (context)
27    pub context: Context,
28    /// Entry actions for states
29    #[serde(skip)]
30    pub(crate) entry_actions: HashMap<String, Vec<Action>>,
31    /// Exit actions for states
32    #[serde(skip)]
33    pub(crate) exit_actions: HashMap<String, Vec<Action>>,
34    /// History states mapping (state id -> last active child)
35    pub(crate) history: HashMap<String, String>,
36    /// マッピング関数: 状態IDから型Sへ変換するための関数
37    #[serde(skip)]
38    #[serde(default)]
39    state_mapper: Option<fn(&str) -> S>,
40    /// 現在の状態のキャッシュ
41    #[serde(skip)]
42    current_state_cache: Option<S>,
43    /// The type markers
44    #[serde(skip)]
45    _phantom_s: std::marker::PhantomData<S>,
46    #[serde(skip)]
47    _phantom_e: std::marker::PhantomData<E>,
48}
49
50impl<S, E> Machine<S, E>
51where
52    S: Clone + 'static + Default,
53    E: Clone + 'static,
54{
55    /// Create a new state machine instance from a builder
56    pub fn new<BuilderS, BuilderE>(builder: MachineBuilder<BuilderS, BuilderE>) -> Result<Self>
57    where
58        BuilderS: Clone + 'static + Default,
59        BuilderE: Clone + 'static,
60    {
61        let MachineBuilder {
62            name,
63            states,
64            transitions,
65            initial,
66            entry_actions,
67            exit_actions,
68            context,
69            _phantom_s: _,
70            _phantom_e: _,
71        } = builder;
72
73        if states.is_empty() {
74            return Err(Error::InvalidConfiguration("No states defined".into()));
75        }
76
77        if !states.contains_key(&initial) {
78            return Err(Error::StateNotFound(initial.clone()));
79        }
80
81        let mut machine = Self {
82            name,
83            states,
84            transitions,
85            initial,
86            current_states: HashSet::new(),
87            context: context.unwrap_or_else(Context::new),
88            entry_actions,
89            exit_actions,
90            history: HashMap::new(),
91            state_mapper: None,
92            current_state_cache: None,
93            _phantom_s: std::marker::PhantomData,
94            _phantom_e: std::marker::PhantomData,
95        };
96
97        // Initialize by entering the initial state
98        machine.initialize()?;
99
100        Ok(machine)
101    }
102
103    /// Initialize the machine by entering the initial state
104    fn initialize(&mut self) -> Result<()> {
105        let initial_state_id = self.initial.clone();
106        self.enter_state(&initial_state_id, &Event::new("init"))?;
107        Ok(())
108    }
109
110    /// Send an event to the machine
111    pub fn send<EV: IntoEvent>(&mut self, event: EV) -> Result<bool> {
112        let event = event.into_event();
113        let mut processed = false;
114
115        // Create a copy of current states to iterate over
116        let current_states: Vec<_> = self.current_states.iter().cloned().collect();
117
118        for state_id in current_states {
119            if self.process_state_event(&state_id, &event)? {
120                processed = true;
121            }
122        }
123
124        // 状態が変更された場合、キャッシュをクリア
125        if processed {
126            self.current_state_cache = None;
127        }
128
129        Ok(processed)
130    }
131
132    /// Process an event for a specific state
133    fn process_state_event(&mut self, state_id: &str, event: &Event) -> Result<bool> {
134        // Find enabled transitions for this state
135        let enabled_transition = self
136            .transitions
137            .iter()
138            .find(|t| t.source == state_id && t.is_enabled(&self.context, event))
139            .cloned();
140
141        if let Some(transition) = enabled_transition {
142            // Execute the transition
143            self.execute_transition(&transition, event)?;
144            Ok(true)
145        } else {
146            // No transitions found for this state, check parent
147            if let Some(parent_id) = self.get_parent_id(state_id) {
148                self.process_state_event(&parent_id, event)
149            } else {
150                Ok(false)
151            }
152        }
153    }
154
155    /// Execute a transition
156    fn execute_transition(&mut self, transition: &Transition, event: &Event) -> Result<()> {
157        let source_id = transition.source.clone();
158
159        // For internal transitions, just execute the actions
160        if transition.target.is_none() {
161            transition.execute_actions(&mut self.context, event);
162            return Ok(());
163        }
164
165        let target_id = transition
166            .target
167            .as_ref()
168            .ok_or_else(|| Error::InvalidTransition("No target state".into()))?
169            .clone();
170
171        // Exit source state
172        self.exit_state(&source_id, event)?;
173
174        // Execute transition actions
175        transition.execute_actions(&mut self.context, event);
176
177        // Enter target state
178        self.enter_state(&target_id, event)?;
179
180        Ok(())
181    }
182
183    /// Enter a state and its initial substates if applicable
184    fn enter_state(&mut self, state_id: &str, event: &Event) -> Result<()> {
185        let state = self
186            .states
187            .get(state_id)
188            .ok_or_else(|| Error::StateNotFound(state_id.to_string()))?
189            .clone();
190
191        // Add to current states
192        self.current_states.insert(state_id.to_string());
193
194        // Execute entry actions
195        if let Some(actions) = self.entry_actions.get(state_id) {
196            for action in actions.clone() {
197                action.execute(&mut self.context, event);
198            }
199        }
200
201        // Handle different state types
202        match state.state_type {
203            StateType::Compound => {
204                // Enter initial substate
205                if let Some(initial) = state.initial {
206                    self.enter_state(&initial, event)?;
207                } else {
208                    return Err(Error::InvalidConfiguration(format!(
209                        "Compound state '{}' has no initial state",
210                        state_id
211                    )));
212                }
213            }
214            StateType::Parallel => {
215                // Enter all child states
216                for child_id in state.children {
217                    self.enter_state(&child_id, event)?;
218                }
219            }
220            StateType::History => {
221                // Enter the last active child state if in history, otherwise the parent's initial
222                if let Some(last_active) = self.history.get(state_id).cloned() {
223                    self.enter_state(&last_active, event)?;
224                } else if let Some(parent_id) = self.get_parent_id(state_id) {
225                    let parent = self
226                        .states
227                        .get(&parent_id)
228                        .ok_or_else(|| Error::StateNotFound(parent_id.to_string()))?
229                        .clone();
230
231                    if let Some(initial) = parent.initial {
232                        self.enter_state(&initial, event)?;
233                    }
234                }
235            }
236            StateType::DeepHistory => {
237                // Deep history logic would be more complex in a real implementation
238                // For now, just use the same logic as regular history
239                if let Some(last_active) = self.history.get(state_id).cloned() {
240                    self.enter_state(&last_active, event)?;
241                } else if let Some(parent_id) = self.get_parent_id(state_id) {
242                    let parent = self
243                        .states
244                        .get(&parent_id)
245                        .ok_or_else(|| Error::StateNotFound(parent_id.to_string()))?
246                        .clone();
247
248                    if let Some(initial) = parent.initial {
249                        self.enter_state(&initial, event)?;
250                    }
251                }
252            }
253            _ => {} // Normal and Final states don't have special entry logic
254        }
255
256        Ok(())
257    }
258
259    /// Exit a state and its substates
260    fn exit_state(&mut self, state_id: &str, event: &Event) -> Result<()> {
261        let state = self
262            .states
263            .get(state_id)
264            .ok_or_else(|| Error::StateNotFound(state_id.to_string()))?
265            .clone();
266
267        // Record in history if it has a parent
268        if let Some(parent_id) = self.get_parent_id(state_id) {
269            self.history.insert(parent_id, state_id.to_string());
270        }
271
272        // First exit children (if any) in reverse order
273        match state.state_type {
274            StateType::Compound | StateType::Parallel => {
275                // Get all active children
276                let active_children: Vec<_> = state
277                    .children
278                    .iter()
279                    .filter(|child_id| self.current_states.contains(*child_id))
280                    .cloned()
281                    .collect();
282
283                // Exit each active child
284                for child_id in active_children {
285                    self.exit_state(&child_id, event)?;
286                }
287            }
288            _ => {} // Other state types don't have children to exit
289        }
290
291        // Execute exit actions
292        if let Some(actions) = self.exit_actions.get(state_id) {
293            for action in actions.clone() {
294                action.execute(&mut self.context, event);
295            }
296        }
297
298        // Remove from current states
299        self.current_states.remove(state_id);
300
301        Ok(())
302    }
303
304    /// Get the parent id of a state
305    fn get_parent_id(&self, state_id: &str) -> Option<String> {
306        self.states
307            .get(state_id)
308            .and_then(|state| state.parent.clone())
309    }
310
311    /// Check if a state is active
312    pub fn is_in(&self, state_id: &str) -> bool {
313        self.current_states.contains(state_id)
314    }
315
316    /// Serialize the machine to JSON
317    pub fn to_json(&self) -> Result<String> {
318        let json = serde_json::to_string_pretty(self)?;
319        Ok(json)
320    }
321
322    /// Deserialize the machine from JSON
323    pub fn from_json(json: &str) -> Result<Self> {
324        let machine: Self = serde_json::from_str(json)?;
325        Ok(machine)
326    }
327
328    /// 状態IDから型Sへのマッピング関数を設定
329    pub fn with_state_mapper(mut self, mapper: fn(&str) -> S) -> Self
330    {
331        self.state_mapper = Some(mapper);
332        self
333    }
334
335    /// Get the current state
336    pub fn current_state(&self) -> S {
337        // 現在アクティブな状態IDを取得
338        if self.current_states.is_empty() {
339            // 初期化されていない場合はエラー
340            panic!("ステートマシンが初期化されていません。send() を呼び出す前に initialize() を呼び出してください。");
341        }
342        
343        // アクティブな状態の中から最初の一つを取得
344        // 注: より複雑な状態階層では、最も具体的な(leaf)状態を選択するロジックが必要かもしれません
345        let state_id = self.current_states.iter().next().unwrap();
346        
347        // 状態IDからS型への変換
348        if let Some(mapper) = self.state_mapper {
349            // 現在の状態を返す(所有権を移す)
350            mapper(state_id)
351        } else {
352            // マッパーが設定されていない場合はエラー
353            panic!("状態マッパーが設定されていません。Machine::with_state_mapper()を使用してマッパーを設定してください。");
354        }
355    }
356
357    /// Apply a transition with the given event
358    pub fn transition<EV: IntoEvent>(&mut self, event: EV, context: Context) -> Result<S> {
359        self.context = context;
360        let event = event.into_event();
361        let result = self.send(event)?;
362        
363        // 状態が変更された場合、キャッシュをクリア
364        if result {
365            self.current_state_cache = None;
366        }
367        
368        Ok(self.current_state())
369    }
370}
371
372/// Builder for constructing state machines
373pub struct MachineBuilder<S = State, E = Event>
374where
375    S: Clone + 'static + Default,
376    E: Clone + 'static,
377{
378    /// Name of the machine
379    pub name: String,
380    /// Collection of states
381    pub states: HashMap<String, State>,
382    /// Collection of transitions
383    pub transitions: Vec<Transition>,
384    /// Initial state id
385    pub initial: String,
386    /// Context for the machine
387    pub context: Option<Context>,
388    /// Entry actions for states
389    pub(crate) entry_actions: HashMap<String, Vec<Action>>,
390    /// Exit actions for states
391    pub(crate) exit_actions: HashMap<String, Vec<Action>>,
392    /// Type markers
393    _phantom_s: std::marker::PhantomData<S>,
394    _phantom_e: std::marker::PhantomData<E>,
395}
396
397impl<S, E> MachineBuilder<S, E>
398where
399    S: Clone + 'static + Default,
400    E: Clone + 'static,
401{
402    /// Create a new state machine builder
403    pub fn new(name: impl Into<String>) -> Self {
404        Self {
405            name: name.into(),
406            states: HashMap::new(),
407            transitions: Vec::new(),
408            initial: String::new(),
409            context: None,
410            entry_actions: HashMap::new(),
411            exit_actions: HashMap::new(),
412            _phantom_s: std::marker::PhantomData,
413            _phantom_e: std::marker::PhantomData,
414        }
415    }
416
417    /// Set the initial state
418    pub fn initial(mut self, state_id: impl Into<String>) -> Self {
419        self.initial = state_id.into();
420        self
421    }
422
423    /// Add a state to the machine
424    pub fn state(mut self, state: State) -> Self {
425        if self.states.is_empty() && self.initial.is_empty() {
426            self.initial = state.id.clone();
427        }
428        self.states.insert(state.id.clone(), state);
429        self
430    }
431
432    /// Add a transition to the machine
433    pub fn transition(mut self, transition: Transition) -> Self {
434        self.transitions.push(transition);
435        self
436    }
437
438    /// Add an entry action to a state
439    pub fn on_entry<A: IntoAction>(mut self, state_id: impl Into<String>, action: A) -> Self {
440        let state_id = state_id.into();
441        let action = action.into_action(ActionType::Entry);
442        self.entry_actions
443            .entry(state_id)
444            .or_default()
445            .push(action);
446        self
447    }
448
449    /// Add an exit action to a state
450    pub fn on_exit<A: IntoAction>(mut self, state_id: impl Into<String>, action: A) -> Self {
451        let state_id = state_id.into();
452        let action = action.into_action(ActionType::Exit);
453        self.exit_actions
454            .entry(state_id)
455            .or_default()
456            .push(action);
457        self
458    }
459
460    /// Set the context for the machine
461    pub fn context(mut self, context: Context) -> Self {
462        self.context = Some(context);
463        self
464    }
465
466    /// Build the state machine
467    pub fn build(self) -> Result<Machine<S, E>> {
468        Machine::new(self)
469    }
470}