Expand description
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);