macro_machine/
lib.rs

1#![deny(unused_must_use)]
2//! State machine generator
3//!
4//! State machine consists of:
5//! Name, initial state, List of States, List of Commands, list of States Nodes.
6//! Each State Node contain: Name, State Context (optional), list of Command Reactions.
7//! Each Command Reaction contain: Command to react on, user-defined code of reaction (optional) and
8//!     next State of machine (optional).
9//!
10//! Simplest state machine example:
11//!
12//! ```
13//! #[macro_use] extern crate macro_machine;
14//!
15//! declare_machine!(
16//!     Simple (A) // Name and initial State
17//!     states[A,B] // list of States
18//!     commands[Next] // list of Commands
19//!     (A: // State Node
20//!         Next => B; // Command Reaction. Just change state to B
21//!     )
22//!     (B:
23//!         Next => A;
24//!     )
25//! );
26//!
27//! # fn main() {
28//! use Simple::*;
29//!
30//! let mut machine = Simple::new();
31//! assert!(match machine.get_current_state(){States::A{..}=>true,_=>false});
32//! machine.execute(&Simple::Commands::Next).unwrap();
33//! assert!(match machine.get_current_state(){States::B{..}=>true,_=>false});
34//! machine.execute(&Simple::Commands::Next).unwrap();
35//! assert!(match machine.get_current_state(){States::A{..}=>true,_=>false});
36//! # }
37//! ```
38//!
39//! You can add some intelligence to machine:
40//!
41//! ```
42//! #[macro_use] extern crate macro_machine;
43//!
44//! declare_machine!(
45//!     Simple (A{counter:0}) // Name and initial State with initial value
46//!     states[A,B] // list of States
47//!     commands[Next] // list of Commands
48//!     (A context{counter:i16}: // State Node and this state context description with binding name
49//!         Next {context.counter=context.counter+1}=> B{counter:context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our x value.
50//!     )
51//!     (B context{counter:i16}:
52//!         Next {context.counter=context.counter+1}=> A{counter:context.counter};
53//!     )
54//! );
55//!
56//! # fn main() {
57//! use Simple::*;
58//!
59//! let mut machine = Simple::new();
60//! assert!(match machine.get_current_state(){
61//!     States::A{context}=> if context.counter == 0 {true} else {false}, // We are in state A and have our initial value 0
62//!     _=>false
63//! });
64//! machine.execute(&Simple::Commands::Next).unwrap();
65//! assert!(match machine.get_current_state(){
66//!     States::B{context}=> if context.counter == 1 {true} else {false}, // We are in state B and have counter == 1
67//!     _=>false
68//! });
69//! machine.execute(&Simple::Commands::Next).unwrap();
70//! assert!(match machine.get_current_state(){
71//!     States::A{context}=> if context.counter == 2 {true} else {false}, // Again in state A and have counter == 2
72//!     _=>false
73//! });
74//! # }
75//! ```
76//! ```
77//! #[macro_use] extern crate macro_machine;
78//!
79//! declare_machine!(
80//!     Simple (A{counter:0}) // Name and initial State with initial value
81//!     states[A,B] // list of States
82//!     commands[Next] // list of Commands
83//!     (A context{counter:i16}: // State Node and this state context description with binding name
84//!         >> {context.counter = context.counter+1;} // Execute when enter state A
85//!         << {context.counter = context.counter+1;} // Execute when leave state A
86//!         Next {context.counter=context.counter+1;} => B{counter:context.counter}; // Command Reaction. Now on command Next we add 1 to our context. Also we change state to B and init it with our x value.
87//!     )
88//!     (B context{counter:i16}:
89//!         Next {context.counter=context.counter+1} => A{counter:context.counter};
90//!     )
91//! );
92//!
93//! # fn main() {
94//! use Simple::*;
95//!
96//! let mut machine = Simple::new();
97//! assert!(match machine.get_current_state(){
98//!
99//!     // We are in state A and have value 1. Because Enter State callback executed.
100//!
101//!     States::A{context}=> if context.counter == 1 {true} else {false},
102//!     _=>false
103//! });
104//! machine.execute(&Simple::Commands::Next).unwrap();
105//! assert!(match machine.get_current_state(){
106//!
107//!     // We are in state B and have counter == 3. Increment happen on User Code execution. Execution of Leave state callback happen after we transfer data to the next state.
108//!
109//!     States::B{context}=> {println!("context counter: {}", context.counter);if context.counter == 3 {true} else {false}},
110//!     _=>false
111//! });
112//! machine.execute(&Simple::Commands::Next).unwrap();
113//! assert!(match machine.get_current_state(){
114//!
115//!     // Again in state A and have counter == 5. Increment happen on User Code execution and on state A enter.
116//!
117//!     States::A{context}=> if context.counter == 5 {true} else {false},
118//!     _=>false
119//! });
120//! # }
121//! ```
122//!
123//! Example of Machine-scoped context. This context exist in machine life-time.
124//!
125//! Lets count machine's state changes:
126//!
127//! ```
128//! #[macro_use] extern crate macro_machine;
129//!
130//! declare_machine!(
131//!     Simple machine_context{counter: i16} (A) // Declare machine scoped context
132//!     states[A,B]
133//!     commands[Next]
134//!     (A :
135//!         >> {machine_context.counter=machine_context.counter+1;} // Add 1 when enter in state
136//!         Next => B; // Just switch to other state
137//!     )
138//!     (B :
139//!         >> {machine_context.counter=machine_context.counter+1;}
140//!         Next => A;
141//!     )
142//! );
143//!
144//! # fn main() {
145//! use Simple::*;
146//!
147//! let mut machine = Simple::new(0);
148//! let context = machine.get_inner_context();
149//! assert!(context.counter == 1);
150//! machine.execute(&Simple::Commands::Next).unwrap();
151//! let context = machine.get_inner_context();
152//! assert!(context.counter == 2);
153//! machine.execute(&Simple::Commands::Next).unwrap();
154//! let context = machine.get_inner_context();
155//! assert!(context.counter == 3);
156//! # }
157//! ```
158//!
159
160#[macro_export]
161macro_rules! declare_machine {
162
163    // Initialize state by values
164    (@inner next $new_state:ident{$($new_el:ident:$new_el_val:expr),*}) => (
165        $new_state{$($new_el:$new_el_val),*}
166    );
167
168    // if state have no fields to initialize. Just for remove redundant curl braces.
169    (@inner next $new_state:ident) => (
170        $new_state{}
171    );
172
173    // If Event have user-defined code and move machine to new state. Execute code and return new state.
174    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
175        {
176            declare_machine!(@inner context $sel $cur);
177            $callback;
178            $cur.leave($glob_context).unwrap();
179            Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
180        }
181    );
182
183    // If Event have user-defined code and don't move machine to new state. Execute code and return __SameState__ .
184    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;) => (
185        {
186            declare_machine!(@inner context $sel $cur);
187            $callback;
188            Some(States::__SameState__)
189        }
190    );
191
192    // If Event have no user-defined code and move machine to new state. Just return new state.
193    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident; ;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
194        {
195            declare_machine!(@inner context $sel $cur);
196            $cur.leave($glob_context).unwrap();
197            Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
198        }
199    );
200
201    // If Event have nothing to do on event. Just return __SameState__.
202    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident ; ;) => (
203        Some(States::__SameState__)
204    );
205
206    // If Event have user-defined code and move machine to new state. Execute code and return new state.
207    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
208        {
209            declare_machine!(@inner context $sel $cur);
210            $callback;
211            $cur.leave($glob_context).unwrap();
212            Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
213        }
214    );
215
216    // If Event have user-defined code and don't move machine to new state. Execute code and return __SameState__ .
217    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident;$callback:block;) => (
218        {
219            declare_machine!(@inner context $sel $cur);
220            $callback;
221            Some(States::__SameState__)
222        }
223    );
224
225    // If Event have no user-defined code and move machine to new state. Just return new state.
226    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident; ;$new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*) => (
227        {
228            declare_machine!(@inner context $sel $cur);
229            $cur.leave($glob_context).unwrap();
230            Some(States::$new_state{context: declare_machine!(@inner next $new_state$({$($new_el:$new_el_val),*})*)})
231        }
232    );
233
234    // If Event have nothing to do on event. Just return __SameState__.
235    (@inner command @$glob_context:ident@ $sel:ident:$cur:ident ; ;) => (
236        Some(States::__SameState__)
237    );
238
239    (@inner context $ss:ident $sel:ident)=>(let $sel = $ss;);
240    (@inner context $ss:ident )=>();
241
242    // Enter/Leave processors with and without user-defined code.
243    (@inner >> $($sel:ident)* @$glob_context:ident@ $income:block) => (
244            fn enter(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
245                declare_machine!(@inner context self $($sel)*);
246                $income
247                Ok(())
248            }
249    );
250    (@inner << $($sel:ident)* @$glob_context:ident@ $outcome:block) => (
251            fn leave(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
252                declare_machine!(@inner context self $($sel)*);
253                $outcome
254                Ok(())
255            }
256    );
257    (@inner >> $($sel:ident)* @$glob_context:ident@ ) => (
258            fn enter(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
259                Ok(())
260            }
261    );
262    (@inner << $($sel:ident)* @$glob_context:ident@ ) => (
263            fn leave(&mut self, $glob_context: &mut MachineContext) -> Result<(), ()> {
264                Ok(())
265            }
266    );
267
268    // This structs keep user-defined contexts for states.
269    (@inner params $state:ident {$($el:ident:$typ:ty);*}) => (
270        #[derive(Debug)]
271        #[derive(PartialEq)]
272        #[derive(Copy)]
273        #[derive(Clone)]
274        pub struct $state {pub $($el:$typ),*}
275    );
276    (@inner params $state:ident) => (
277        #[derive(Debug)]
278        #[derive(PartialEq)]
279        #[derive(Copy)]
280        #[derive(Clone)]
281        pub struct $state {}
282    );
283    (@inner initial $initial:ident{$($init_field:ident:$init_val:expr),*}) => ($initial{$($init_field: $init_val),*});
284    (@inner initial $initial:ident) => ($initial{});
285
286    (@cmd_processor $sel:ident @$glob_context:ident@ ($($cmd:ident $($callback:block)* => $($new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*)*;)*))=>(
287        fn do_job(&mut self, cmd: & Commands, $glob_context: &mut MachineContext) -> Option<States> {
288            match *cmd {
289                $(Commands::$cmd => {declare_machine!(@inner command @$glob_context@ self:$sel;$($callback)*;$($new_state$({$($new_el:$new_el_val),*})*)*)})*
290                _ => None
291            }
292        }
293    );
294
295    (@state $gc_name:ident; $($state:ident @ $sel:ident ; $($income:block)*; ($job:tt); $($outcome:block)*@),*) => (
296        $(
297        impl CanDoJob for $state {
298            declare_machine!(@cmd_processor $sel @$gc_name@ $job);
299            declare_machine!(@inner >> $sel @$gc_name@ $($income)*);
300            declare_machine!(@inner << $sel @$gc_name@ $($outcome)*);
301        }
302        )*
303    );
304    (@state ; $($state:ident @ $sel:ident ; $($income:block)*; ($job:tt); $($outcome:block)* @),*) => (
305        $(
306        impl CanDoJob for $state {
307            declare_machine!(@cmd_processor $sel @__@ $job);
308            declare_machine!(@inner >> $sel @__@ $($income)*);
309            declare_machine!(@inner << $sel @__@ $($outcome)*);
310        }
311        )*
312    );
313
314    (@state $gc_name:ident; $($state:ident@; $($income:block)*; ($job:tt); $($outcome:block)*@),*) => (
315        $(
316        impl CanDoJob for $state {
317            declare_machine!(@cmd_processor ___ @$gc_name@ $job);
318            declare_machine!(@inner >> ___ @$gc_name@ $($income)*);
319            declare_machine!(@inner << ___ @$gc_name@ $($outcome)*);
320        }
321        )*
322    );
323    (@state ; $($state:ident@; $($income:block)*; ($job:tt); $($outcome:block)*@),*) => (
324        $(
325        impl CanDoJob for $state {
326            declare_machine!(@cmd_processor ___ @__@ $job);
327            declare_machine!(@inner >> ___ @__@ $($income)*);
328            declare_machine!(@inner << ___ @__@ $($outcome)*);
329        }
330        )*
331    );
332
333// Main pattern
334
335(
336    $machine:ident $($gc_name:ident{$($context_field:ident:$context_type:ty),*})* ($initial:ident$({$($init_field:ident:$init_val:expr),*})*)
337    states[$($states:ident),*]
338    commands[$($commands:ident),*]
339
340    $(($state:ident $($sel:ident)*$({$($el:ident:$typ:ty);*})*:
341        $(>> $income:block)*
342        $(<< $outcome:block)*
343        $($cmd:ident $($callback:block)* => $($new_state:ident$({$($new_el:ident:$new_el_val:expr),*})*)*;)*
344    ))*
345) => (
346    #[allow(non_snake_case)]
347    #[allow(unused_imports)]
348    #[allow(dead_code)]
349    #[allow(unused_variables)]
350    mod $machine {
351        use super::*;
352        trait CanDoJob {
353            fn do_job(&mut self, cmd: &Commands, global_context: &mut MachineContext) -> Option<States>;
354            fn leave(&mut self, &mut MachineContext) -> Result<(), ()>;
355            fn enter(&mut self, &mut MachineContext) -> Result<(), ()>;
356        }
357
358        $(
359        declare_machine!(@inner params $state $({$($el:$typ);*})*);
360        )*
361
362        declare_machine!(@state $($gc_name)*;$($state @ $($sel)* ; $($income)*; (($($cmd $($callback)* => $($new_state $({$($new_el:$new_el_val),*})*)*;)*)); $($outcome)*@),*);
363
364        #[derive(Debug)]
365        #[derive(PartialEq)]
366        #[derive(Copy)]
367        #[derive(Clone)]
368        pub enum States {
369            __SameState__,
370            $($states {context: $states}),*
371        }
372
373        #[derive(Debug)]
374        #[derive(PartialEq)]
375        pub enum Commands {
376            $($commands),*
377        }
378
379        #[derive(Clone)]
380        pub struct MachineContext {$($(pub $context_field: $context_type),*)*}
381
382        pub struct Machine {
383            state: States,
384            context: MachineContext
385        }
386        pub fn new($($($context_field: $context_type),*)*) -> Machine {
387            let mut context = declare_machine!(@inner initial $initial $({$($init_field: $init_val),*})*);
388            let mut machine_context = MachineContext{$($($context_field: $context_field),*)*};
389            context.enter(&mut machine_context).unwrap();
390            Machine{state: States::$initial{context: context}, context: machine_context}
391        }
392
393        impl Machine {
394            pub fn execute(&mut self, cmd: & Commands) -> Result<(),()>{
395                match {
396                    match self.state {
397                        States::__SameState__ => None,
398                        $(States::$state{ ref mut context } => context.do_job(cmd, &mut self.context)),*
399                    }
400                } {
401                    Some(x) => {
402                        match x {
403                            States::__SameState__ => {},
404                            _ => {
405                                self.change_state(x)
406                            }
407                        };Ok(())
408                    },
409                    None => {println!("Wrong operation {:?} for {:?} state!", cmd, self.state); Err(())}
410                }
411            }
412            fn change_state(&mut self, new_state: States) {
413                self.state = new_state;
414                match self.state {
415                    States::__SameState__ => Ok(()),
416                    $(States::$state{ ref mut context } => context.enter(&mut self.context)),*
417                }.unwrap();
418            }
419            pub fn get_current_state(&self) -> States {
420                self.state.clone()
421            }
422            pub fn get_inner_context(&self) -> MachineContext {
423                self.context.clone()
424            }
425        }
426    }
427)
428}
429
430#[cfg(test)]
431mod tests {
432    fn tes(x:i16) {
433        println!("x:{}",x);
434    }
435
436    declare_machine!(
437    Mach1 (New{x:0})
438
439    states[New,InConfig,Operational]
440    commands[Configure, ConfigureDone, Drop]
441
442    ( New context{x: i16}:
443        >> {println!("Enter {:?}", context)}
444        << {println!("Leave {:?}", context)}
445        Configure     {println!("In New with context val: {}", context.x);} =>     InConfig{x:context.x+1, y:0};
446        ConfigureDone => New{x:0};
447    )
448    ( InConfig context{x:i16; y:i16}:
449        >> {println!("Enter {:?}", context)}
450        << {println!("Leave {:?}", context)}
451        ConfigureDone {tes(context.x)}=> Operational;
452    )
453    ( Operational context:
454        >> {println!("Enter {:?}", context)}
455        << {println!("Leave {:?}", context)}
456        ConfigureDone =>;
457        Drop => New{x:0};
458    )
459    );
460
461    declare_machine!(
462    Mach2 (State1)
463
464    states[State1,State2,State3]
465    commands[ToState1, ToState2, ToState3]
466
467    ( State1 :
468        ToState2 => State2;
469    )
470    ( State2 :
471        ToState3 => State3;
472    )
473    ( State3 :
474        ToState1 => State1;
475    )
476    );
477
478    #[test]
479    fn test1() {
480        let mut m = Mach1::new();
481        m.execute(&Mach1::Commands::Configure).unwrap();
482        m.execute(&Mach1::Commands::ConfigureDone).unwrap();
483        m.execute(&Mach1::Commands::Drop).unwrap();
484        m.execute(&Mach1::Commands::Configure).unwrap();
485        m.execute(&Mach1::Commands::ConfigureDone).unwrap();
486        m.execute(&Mach1::Commands::ConfigureDone).unwrap();
487        m.execute(&Mach1::Commands::ConfigureDone).unwrap();
488    }
489
490    #[test]
491    #[should_panic]
492    fn test2() {
493        let mut m = Mach2::new();
494        m.execute(&Mach2::Commands::ToState2).unwrap();
495        m.execute(&Mach2::Commands::ToState3).unwrap();
496        m.execute(&Mach2::Commands::ToState1).unwrap();
497        m.execute(&Mach2::Commands::ToState3).unwrap();
498    }
499
500    declare_machine!(
501    Mach3 glob_cont{id:i16}(State1{counter:0})
502
503    states[State1,State2,State3]
504    commands[ToState1, ToState2, ToState3]
505
506    ( State1 cont{counter:i16}:
507        >>{println!("Mach {} enter {:?}", glob_cont.id, cont);}
508        <<{cont.counter+=1; println!("Mach {} leave {:?}", glob_cont.id, cont);}
509        ToState2 => State2{counter: cont.counter};
510    )
511    ( State2 cont{counter:i16}:
512        >>{println!("Mach {} enter {:?}", glob_cont.id, cont);}
513        <<{cont.counter+=1; println!("Mach {} leave {:?}", glob_cont.id, cont);}
514        ToState3 => State3{counter: cont.counter};
515    )
516    ( State3 cont{counter:i16}:
517        >>{println!("Mach {} enter {:?}", glob_cont.id, cont);}
518        <<{cont.counter+=1; println!("Mach {} leave {:?}", glob_cont.id, cont);}
519        ToState1 => State1{counter: cont.counter};
520    )
521    );
522
523    #[test]
524    fn test3() {
525        let mut m = Mach3::new(0);
526        let mut m1 = Mach3::new(1);
527        m1.execute(&Mach3::Commands::ToState2).unwrap();
528        m.execute(&Mach3::Commands::ToState2).unwrap();
529        m.execute(&Mach3::Commands::ToState3).unwrap();
530        m1.execute(&Mach3::Commands::ToState3).unwrap();
531    }
532
533    #[derive(Clone)]
534    pub struct InnerMachineContext {
535        id: i16,
536        name: String,
537        counter: i16
538    }
539
540    declare_machine!(
541        Mach4 inner{st: InnerMachineContext} (State1)
542        states[State1,State2,State3]
543        commands[ToState1, ToState2, ToState3]
544
545        ( State1 :
546            << {println!("id={} name={} counter={}", inner.st.id, inner.st.name, inner.st.counter);}
547            ToState2 {inner.st.counter+=1;}=> State2;
548        )
549        ( State2 :
550            << {println!("id={} name={} counter={}", inner.st.id, inner.st.name, inner.st.counter);}
551            ToState3 {inner.st.counter+=1;}=> State3;
552        )
553        ( State3 :
554            << {println!("id={} name={} counter={}", inner.st.id, inner.st.name, inner.st.counter);}
555            ToState1 {inner.st.counter+=1;}=> State1;
556        )
557    );
558    #[test]
559    fn test4() {
560        let mut m = Mach4::new(InnerMachineContext{id:0, name: String::from("Mach 0"), counter: 0});
561        let mut m1 = Mach4::new(InnerMachineContext{id:1, name: String::from("Mach 1"), counter: 0});
562        let mut m2 = Mach4::new(InnerMachineContext{id:2, name: String::from("Mach 2"), counter: 0});
563        m.execute(&Mach4::Commands::ToState2).unwrap();
564        m.execute(&Mach4::Commands::ToState3).unwrap();
565        m1.execute(&Mach4::Commands::ToState2).unwrap();
566        m.execute(&Mach4::Commands::ToState1).unwrap();
567        m1.execute(&Mach4::Commands::ToState3).unwrap();
568        m2.execute(&Mach4::Commands::ToState2).unwrap();
569        m.execute(&Mach4::Commands::ToState2).unwrap();
570        m2.execute(&Mach4::Commands::ToState3).unwrap();
571        m1.execute(&Mach4::Commands::ToState1).unwrap();
572    }
573}