Crate macro_machine [−] [src]
State machine generator
State machine consists of: Name, initial state, List of States, List of Commands, list of States Nodes. Each State Node contain: Name, State Context (optional), list of Command Reactions. Each Command Reaction contain: Command to react on, user-defined code of reaction (optional) and next State of machine (optional).
Simplest state machine example:
#[macro_use] extern crate macro_machine; declare_machine!( Simple (A) // Name and initial State states[A,B] // list of States commands[Next] // list of Commands (A: // State Node Next => B; // Command Reaction. Just change state to B ) (B: Next => A; ) ); use Simple::*; let mut machine = Simple::new(); assert!(match machine.get_current_state(){States::A{..}=>true,_=>false}); machine.execute(&Simple::Commands::Next).unwrap(); assert!(match machine.get_current_state(){States::B{..}=>true,_=>false}); machine.execute(&Simple::Commands::Next).unwrap(); assert!(match machine.get_current_state(){States::A{..}=>true,_=>false});
You can add some intelligence to machine:
#[macro_use] extern crate macro_machine; declare_machine!( Simple (A{counter:0}) // Name and initial State with initial value states[A,B] // list of States commands[Next] // list of Commands (A context{counter:i16}: // State Node and this state context description with binding name 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. ) (B context{counter:i16}: Next {context.counter=context.counter+1}=> A{counter:context.counter}; ) ); use Simple::*; let mut machine = Simple::new(); assert!(match machine.get_current_state(){ States::A{context}=> if context.counter == 0 {true} else {false}, // We are in state A and have our initial value 0 _=>false }); machine.execute(&Simple::Commands::Next).unwrap(); assert!(match machine.get_current_state(){ States::B{context}=> if context.counter == 1 {true} else {false}, // We are in state B and have counter == 1 _=>false }); machine.execute(&Simple::Commands::Next).unwrap(); assert!(match machine.get_current_state(){ States::A{context}=> if context.counter == 2 {true} else {false}, // Again in state A and have counter == 2 _=>false });
#[macro_use] extern crate macro_machine; declare_machine!( Simple (A{counter:0}) // Name and initial State with initial value states[A,B] // list of States commands[Next] // list of Commands (A context{counter:i16}: // State Node and this state context description with binding name >> {context.counter = context.counter+1;} // Execute when enter state A << {context.counter = context.counter+1;} // Execute when leave state A 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. ) (B context{counter:i16}: Next {context.counter=context.counter+1} => A{counter:context.counter}; ) ); use Simple::*; let mut machine = Simple::new(); assert!(match machine.get_current_state(){ // We are in state A and have value 1. Because Enter State callback executed. States::A{context}=> if context.counter == 1 {true} else {false}, _=>false }); machine.execute(&Simple::Commands::Next).unwrap(); assert!(match machine.get_current_state(){ // 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. States::B{context}=> {println!("context counter: {}", context.counter);if context.counter == 3 {true} else {false}}, _=>false }); machine.execute(&Simple::Commands::Next).unwrap(); assert!(match machine.get_current_state(){ // Again in state A and have counter == 5. Increment happen on User Code execution and on state A enter. States::A{context}=> if context.counter == 5 {true} else {false}, _=>false });
Example of Machine-scoped context. This context exist in machine life-time.
Lets count machine's state changes:
#[macro_use] extern crate macro_machine; declare_machine!( Simple machine_context{counter: i16} (A) // Declare machine scoped context states[A,B] commands[Next] (A : >> {machine_context.counter=machine_context.counter+1;} // Add 1 when enter in state Next => B; // Just switch to other state ) (B : >> {machine_context.counter=machine_context.counter+1;} Next => A; ) ); use Simple::*; let mut machine = Simple::new(0); let context = machine.get_inner_context(); assert!(context.counter == 1); machine.execute(&Simple::Commands::Next).unwrap(); let context = machine.get_inner_context(); assert!(context.counter == 2); machine.execute(&Simple::Commands::Next).unwrap(); let context = machine.get_inner_context(); assert!(context.counter == 3);
Macros
declare_machine |