Skip to main content

Machine

Struct Machine 

Source
pub struct Machine<S, E> { /* private fields */ }
Expand description

A generic finite state machine.

§Type Parameters

  • S: The state type. Must implement Eq + Hash + Clone.
  • E: The event type. Must implement Eq + Hash + Clone.

Implementations§

Source§

impl<S, E> Machine<S, E>

Source

pub fn new(initial_state: S) -> Self

Create a new state machine with the given initial state.

Examples found in repository?
examples/callbacks.rs (line 6)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (line 26)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn state(&self) -> &S

A reference to the current state of the machine.

Examples found in repository?
examples/callbacks.rs (line 49)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (line 84)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source§

impl<S, E> Machine<S, E>
where S: Eq + Hash + Clone, E: Eq + Hash + Clone,

Source

pub fn states(&self) -> impl Iterator<Item = &S>

Returns an iterator over all states known to the machine.

Examples found in repository?
examples/order.rs (line 80)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn events(&self) -> impl Iterator<Item = &E>

Returns an iterator over all events the machine can react to.

Examples found in repository?
examples/order.rs (line 81)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn triggerable_events(&self) -> impl Iterator<Item = &E>

Returns an iterator over events valid from the current state.

Only events that have a defined transition from the machine’s current state are included.

Examples found in repository?
examples/order.rs (line 85)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source§

impl<S, E> Machine<S, E>
where S: Eq + Hash + Clone, E: Eq + Hash + Clone,

Source

pub fn when(&mut self, event: E, state: S, new_state: S)

When event occurs in state, move to new_state.

Multiple calls to when for the same (event, state) will overwrite the previous new_state.

Examples found in repository?
examples/callbacks.rs (line 9)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (line 29)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn when_iter<I>(&mut self, event: E, mapping: I)
where I: IntoIterator<Item = (S, S)>,

Define multiple transitions for a single event.

The mapping iterator should yield (from_state, to_state) pairs.

Examples found in repository?
examples/order.rs (lines 34-40)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn on_enter<F>(&mut self, state: S, callback: F)
where F: Fn(E) + 'static,

Register a callback that only cares about the event (no payload).

Examples found in repository?
examples/callbacks.rs (line 13)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
Source

pub fn on_enter_with<P, F>(&mut self, state: S, callback: F)
where P: 'static, F: Fn(E, &P) + 'static,

Register a callback that expects a payload of type P.

The callback takes the triggering event and a payload of type P. It will only be invoked if the payload downcasts to P successfully.

Examples found in repository?
examples/callbacks.rs (lines 17-19)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (lines 44-46)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn on_transition<F>(&mut self, callback: F)
where F: Fn(E) + 'static + Clone,

Register a callback to fire on any state transition.

Works similarly to on_enter, but the callback runs regardless of the specific state entered.

Examples found in repository?
examples/callbacks.rs (line 22)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (lines 65-67)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn on_transition_with<P, F>(&mut self, callback: F)
where P: 'static, F: Fn(E, &P) + 'static + Clone,

Register a callback to fire on any state transition with a payload of type P.

Works similarly to on_enter_with, but the callback runs regardless of the specific state, and only if the payload downcasts to P successfully.

Examples found in repository?
examples/callbacks.rs (lines 23-25)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (lines 70-72)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn trigger(&mut self, event: &E) -> Result<(), MachineError>

Trigger the given event on the machine without any payload.

If the event is defined for the current state, the machine will transition to the corresponding new state and invoke any registered callbacks.

§Errors
Examples found in repository?
examples/callbacks.rs (line 33)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (line 97)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}
Source

pub fn trigger_with<P>( &mut self, event: &E, payload: &P, ) -> Result<(), MachineError>
where P: 'static,

Trigger the given event on the machine with an associated payload.

The payload will be provided to callbacks that accept the payload type P. If the event is defined for the current state, the machine will perform the transition and invoke any matching callbacks.

§Errors
Examples found in repository?
examples/callbacks.rs (line 39)
5fn main() {
6    let mut nano = Machine::new("locked");
7
8    // Define valid transitions.
9    nano.when("insert coin", "locked", "unlocked");
10    nano.when("turn knob", "unlocked", "locked");
11
12    // State-specific callbacks without payload.
13    nano.on_enter("unlocked", |e| println!("Unlocked via {:?}", e));
14    nano.on_enter("locked", |e| println!("Locked via {:?}", e));
15
16    // State-specific callback with a u32 payload.
17    nano.on_enter_with("unlocked", |e, amount: &u32| {
18        println!("Received {} cents via {:?}", amount, e);
19    });
20
21    // Global callbacks.
22    nano.on_transition(|e| println!("Global - event {:?}", e));
23    nano.on_transition_with(|e, amount: &u32| {
24        println!("Global - saw {} cents via {:?}", amount, e);
25    });
26    nano.on_transition_with(|e, msg: &String| {
27        println!("Global - saw \"{}\" via {:?}", msg, e);
28    });
29
30    // Prints:
31    //   Unlocked via "insert coin"
32    //   Global - event "insert coin"
33    nano.trigger(&"insert coin").unwrap();
34
35    // Prints:
36    //   Locked via "turn knob"
37    //   Global - event "turn knob"
38    //   Global - saw "voucher" via "turn knob"
39    nano.trigger_with(&"turn knob", &"voucher".to_string()).unwrap();
40
41    // Prints:
42    //   Unlocked via "insert coin"
43    //   Received 50 cents via "insert coin"
44    //   Global - event "insert coin"
45    //   Global - saw 50 cents via "insert coin"
46    nano.trigger_with(&"insert coin", &50u32).unwrap();
47
48    // Final state: "unlocked"
49    println!("Final state: {:?}", nano.state());
50}
More examples
Hide additional examples
examples/order.rs (line 91)
24fn main() {
25    // Create the machine in the Created state.
26    let mut nano = Machine::new(OrderState::Created);
27
28    // Define transitions one-by-one.
29    nano.when(OrderEvent::Pay, OrderState::Created, OrderState::Paid);
30    nano.when(OrderEvent::Ship, OrderState::Paid, OrderState::Shipped);
31    nano.when(OrderEvent::Deliver, OrderState::Shipped, OrderState::Delivered);
32
33    // Define Cancel for both Created and Paid using when_iter.
34    nano.when_iter(
35        OrderEvent::Cancel,
36        [
37            (OrderState::Created, OrderState::Cancelled),
38            (OrderState::Paid, OrderState::Cancelled),
39        ],
40    );
41
42    // ─── State-specific hooks ───────────────────────────────────────────────
43    // On Paid: log payment amount.
44    nano.on_enter_with(OrderState::Paid, |evt, amount: &u32| {
45        println!("✔ Payment {:?}: {}¢ received", evt, amount);
46    });
47
48    // On Shipped: log tracking code.
49    nano.on_enter_with(OrderState::Shipped, |_evt, tracking: &String| {
50        println!("🚚 Order shipped! Tracking code: {}", tracking);
51    });
52
53    // On Delivered: confirmation.
54    nano.on_enter_with(OrderState::Delivered, |_evt, _: &()| {
55        println!("📦 Order delivered. Thank you!");
56    });
57
58    // On Cancelled: show cancellation reason.
59    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
60        println!("❌ Order cancelled: {}", reason);
61    });
62
63    // ─── Global hooks ───────────────────────────────────────────────────────
64    // Fires on *every* transition (no payload).
65    nano.on_transition(|evt| {
66        println!("🔄 Transition via event: {:?}", evt);
67    });
68
69    // Fires on *every* transition *with* a u32 payload (e.g. payments).
70    nano.on_transition_with(|evt, amount: &u32| {
71        println!("🌐 Global saw {}¢ payment via {:?}", amount, evt);
72    });
73
74    // Fires on *every* transition *with* a String payload
75    // (e.g. tracking codes or cancellation reasons).
76    nano.on_transition_with(|evt, text: &String| {
77        println!("🌐 Global saw string payload {:?}: {}", evt, text);
78    });
79    // Inspect machine metadata:
80    println!("All states: {:?}", nano.states().cloned().collect::<Vec<_>>());
81    println!("All events: {:?}", nano.events().cloned().collect::<Vec<_>>());
82    println!(
83        "Triggerable now ({:?}): {:?}",
84        nano.state(),
85        nano.triggerable_events().cloned().collect::<Vec<_>>()
86    );
87    println!();
88
89    // Simulate a successful order:
90    //    Pay with payload.
91    nano.trigger_with(&OrderEvent::Pay, &2_500u32)
92        .expect("Should transition to Paid");
93    //    Ship with payload.
94    nano.trigger_with(&OrderEvent::Ship, &"TRACK1234".to_string())
95        .expect("Should transition to Shipped");
96    //    Deliver without payload.
97    nano.trigger(&OrderEvent::Deliver).expect("Should transition to Delivered");
98    println!("\nFinal state: {:?}\n", nano.state());
99
100    // Attempt an invalid transition (Pay after Delivered).
101    match nano.trigger(&OrderEvent::Pay) {
102        Ok(_) => println!("Unexpectedly succeeded!"),
103        Err(e) => println!("Error trying to pay in {:?}: {}", nano.state(), e),
104    }
105
106    // Create a fresh order and cancel it early.
107    let mut nano = Machine::new(OrderState::Created);
108    nano.when_iter(
109        OrderEvent::Cancel,
110        [
111            (OrderState::Created, OrderState::Cancelled),
112            (OrderState::Paid, OrderState::Cancelled),
113        ],
114    );
115    // Re-attach only the Cancel callback.
116    nano.on_enter_with(OrderState::Cancelled, |_evt, reason: &String| {
117        println!("Order #2 cancelled reason: {}", reason);
118    });
119    // Cancel with a payload.
120    nano.trigger_with(
121        &OrderEvent::Cancel,
122        &"Customer changed mind".to_string(),
123    )
124    .unwrap();
125
126    println!("Order #2 final: {:?}", nano.state());
127}

Trait Implementations§

Source§

impl<S: Clone, E: Clone> Clone for Machine<S, E>

Source§

fn clone(&self) -> Machine<S, E>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<S: Debug + Eq + Hash + Clone, E: Debug + Eq + Hash + Clone> Debug for Machine<S, E>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<S, E> Default for Machine<S, E>
where S: Default,

Source§

fn default() -> Self

Create a default machine, using S::default() as the initial state.

Auto Trait Implementations§

§

impl<S, E> !RefUnwindSafe for Machine<S, E>

§

impl<S, E> !Send for Machine<S, E>

§

impl<S, E> !Sync for Machine<S, E>

§

impl<S, E> !UnwindSafe for Machine<S, E>

§

impl<S, E> Freeze for Machine<S, E>
where S: Freeze,

§

impl<S, E> Unpin for Machine<S, E>
where S: Unpin, E: Unpin,

§

impl<S, E> UnsafeUnpin for Machine<S, E>
where S: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.