order_example/
order_example.rs

1//! Examples demonstrating different features of the state machine
2
3use rs_statemachine::*;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, Hash, Eq, PartialEq)]
7enum OrderState {
8    New,
9    PaymentPending,
10    PaymentReceived,
11    Processing,
12    Shipped,
13    Delivered,
14    Cancelled,
15    Refunded,
16}
17
18impl State for OrderState {}
19
20#[derive(Debug, Clone, Hash, Eq, PartialEq)]
21enum OrderEvent {
22    Pay,
23    ConfirmPayment,
24    Process,
25    Ship,
26    Deliver,
27    Cancel,
28    Refund,
29}
30
31impl Event for OrderEvent {}
32
33#[derive(Debug, Clone)]
34struct OrderContext {
35    order_id: String,
36    amount: f64,
37    customer_id: String,
38}
39
40impl Context for OrderContext {}
41
42/// Example 1: Basic state machine (no features required)
43fn basic_example() {
44    println!("=== Basic State Machine Example ===");
45
46    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
47
48    builder
49        .external_transition()
50        .from(OrderState::New)
51        .to(OrderState::PaymentPending)
52        .on(OrderEvent::Pay)
53        .perform(|_s, _e, ctx| {
54            println!("Order {} payment initiated", ctx.order_id);
55        });
56
57    builder
58        .external_transition()
59        .from(OrderState::PaymentPending)
60        .to(OrderState::PaymentReceived)
61        .on(OrderEvent::ConfirmPayment)
62        .perform(|_s, _e, ctx| {
63            println!("Payment confirmed for order {}", ctx.order_id);
64        });
65
66    let state_machine = builder.id("BasicOrderMachine").build();
67
68    let context = OrderContext {
69        order_id: "ORD-001".to_string(),
70        amount: 99.99,
71        customer_id: "CUST-123".to_string(),
72    };
73
74    let result = state_machine.fire_event(OrderState::New, OrderEvent::Pay, context.clone());
75    println!("Transition result: {:?}", result);
76}
77
78/// Example 2: With history tracking (requires 'history' feature)
79#[cfg(feature = "history")]
80fn history_example() {
81    println!("\n=== History Tracking Example ===");
82
83    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
84
85    builder
86        .external_transition()
87        .from(OrderState::New)
88        .to(OrderState::PaymentPending)
89        .on(OrderEvent::Pay)
90        .perform(|_s, _e, _c| {});
91
92    builder
93        .external_transition()
94        .from(OrderState::PaymentPending)
95        .to(OrderState::PaymentReceived)
96        .on(OrderEvent::ConfirmPayment)
97        .perform(|_s, _e, _c| {});
98
99    builder
100        .external_transition()
101        .from(OrderState::PaymentReceived)
102        .to(OrderState::Processing)
103        .on(OrderEvent::Process)
104        .perform(|_s, _e, _c| {});
105
106    let state_machine = builder.id("HistoryOrderMachine").build();
107
108    let context = OrderContext {
109        order_id: "ORD-002".to_string(),
110        amount: 149.99,
111        customer_id: "CUST-456".to_string(),
112    };
113
114    // Execute multiple transitions
115    let _ = state_machine.fire_event(OrderState::New, OrderEvent::Pay, context.clone());
116    let _ = state_machine.fire_event(
117        OrderState::PaymentPending,
118        OrderEvent::ConfirmPayment,
119        context.clone(),
120    );
121    let _ = state_machine.fire_event(OrderState::PaymentReceived, OrderEvent::Process, context);
122
123    // Check history
124    let history = state_machine.get_history();
125    println!("Transition history:");
126    for (i, record) in history.iter().enumerate() {
127        println!(
128            "  {}. {:?} -> {:?} via {:?} (success: {})",
129            i + 1,
130            record.from,
131            record.to,
132            record.event,
133            record.success
134        );
135    }
136}
137
138/// Example 3: With entry/exit actions (requires 'extended' feature)
139#[cfg(feature = "extended")]
140fn extended_example() {
141    println!("\n=== Extended State Machine Example ===");
142
143    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
144
145    builder
146        .with_entry_action(OrderState::Processing, |state, ctx| {
147            println!(
148                "ENTRY: Starting to process order {} in state {:?}",
149                ctx.order_id, state
150            );
151        })
152        .with_exit_action(OrderState::Processing, |state, ctx| {
153            println!(
154                "EXIT: Finished processing order {} from state {:?}",
155                ctx.order_id, state
156            );
157        })
158        .with_entry_action(OrderState::Shipped, |_state, ctx| {
159            println!("ENTRY: Order {} has been shipped!", ctx.order_id);
160        });
161
162    builder
163        .external_transition()
164        .from(OrderState::PaymentReceived)
165        .to(OrderState::Processing)
166        .on(OrderEvent::Process)
167        .perform(|_s, _e, _c| {});
168
169    builder
170        .external_transition()
171        .from(OrderState::Processing)
172        .to(OrderState::Shipped)
173        .on(OrderEvent::Ship)
174        .perform(|_s, _e, _c| {});
175
176    let state_machine = builder.id("ExtendedOrderMachine").build();
177
178    let context = OrderContext {
179        order_id: "ORD-003".to_string(),
180        amount: 299.99,
181        customer_id: "CUST-789".to_string(),
182    };
183
184    let _ = state_machine.fire_event(
185        OrderState::PaymentReceived,
186        OrderEvent::Process,
187        context.clone(),
188    );
189    let _ = state_machine.fire_event(OrderState::Processing, OrderEvent::Ship, context);
190}
191
192/// Example 4: With metrics (requires 'metrics' feature)
193#[cfg(feature = "metrics")]
194fn metrics_example() {
195    println!("\n=== Metrics Collection Example ===");
196
197    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
198
199    // Add multiple transitions
200    builder
201        .external_transition()
202        .from(OrderState::New)
203        .to(OrderState::PaymentPending)
204        .on(OrderEvent::Pay)
205        .perform(|_s, _e, _c| {});
206
207    builder
208        .external_transition()
209        .from(OrderState::PaymentPending)
210        .to(OrderState::PaymentReceived)
211        .on(OrderEvent::ConfirmPayment)
212        .when(|_s, _e, ctx| ctx.amount > 0.0)
213        .perform(|_s, _e, _c| {});
214
215    builder
216        .external_transitions()
217        .from_among(vec![
218            OrderState::New,
219            OrderState::PaymentPending,
220            OrderState::Processing,
221        ])
222        .to(OrderState::Cancelled)
223        .on(OrderEvent::Cancel)
224        .perform(|_s, _e, _c| {});
225
226    let state_machine = builder.id("MetricsOrderMachine").build();
227
228    // Simulate multiple orders
229    for i in 0..10 {
230        let context = OrderContext {
231            order_id: format!("ORD-{:03}", i),
232            amount: (i as f64) * 10.0,
233            customer_id: format!("CUST-{:03}", i),
234        };
235
236        let _ = state_machine.fire_event(OrderState::New, OrderEvent::Pay, context.clone());
237
238        if i % 3 == 0 {
239            // Some orders get cancelled
240            let _ =
241                state_machine.fire_event(OrderState::PaymentPending, OrderEvent::Cancel, context);
242        } else {
243            // Others proceed normally
244            let _ = state_machine.fire_event(
245                OrderState::PaymentPending,
246                OrderEvent::ConfirmPayment,
247                context,
248            );
249        }
250    }
251
252    // Get and display metrics
253    let metrics = state_machine.get_metrics();
254    println!("State Machine Metrics:");
255    println!("  Total transitions: {}", metrics.total_transitions);
256    println!("  Successful: {}", metrics.successful_transitions);
257    println!("  Failed: {}", metrics.failed_transitions);
258    println!("  Success rate: {:.2}%", metrics.success_rate() * 100.0);
259
260    if let Some(avg_time) = metrics.average_transition_time() {
261        println!("  Average transition time: {:?}", avg_time);
262    }
263
264    println!("  State visit counts:");
265    for (state, count) in &metrics.state_visit_counts {
266        println!("    {}: {}", state, count);
267    }
268}
269
270/// Example 5: With guard priorities (requires 'guards' feature)
271#[cfg(feature = "guards")]
272fn guards_example() {
273    println!("\n=== Guard Priorities Example ===");
274
275    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
276
277    // Multiple transitions with different priorities
278    builder
279        .external_transition()
280        .from(OrderState::PaymentReceived)
281        .to(OrderState::Processing)
282        .on(OrderEvent::Process)
283        .when(|_s, _e, ctx| ctx.amount < 100.0)
284        .with_priority(10)
285        .perform(|_s, _e, ctx| {
286            println!(
287                "Processing small order {} (amount: {})",
288                ctx.order_id, ctx.amount
289            );
290        });
291
292    builder
293        .external_transition()
294        .from(OrderState::PaymentReceived)
295        .to(OrderState::Processing)
296        .on(OrderEvent::Process)
297        .when(|_s, _e, ctx| ctx.amount >= 100.0 && ctx.amount < 1000.0)
298        .with_priority(20)
299        .perform(|_s, _e, ctx| {
300            println!(
301                "Processing medium order {} (amount: {})",
302                ctx.order_id, ctx.amount
303            );
304        });
305
306    builder
307        .external_transition()
308        .from(OrderState::PaymentReceived)
309        .to(OrderState::Processing)
310        .on(OrderEvent::Process)
311        .when(|_s, _e, ctx| ctx.amount >= 1000.0)
312        .with_priority(30)
313        .perform(|_s, _e, ctx| {
314            println!(
315                "Processing large order {} (amount: {}) - Priority handling!",
316                ctx.order_id, ctx.amount
317            );
318        });
319
320    let state_machine = builder.id("GuardsOrderMachine").build();
321
322    // Test with different order amounts
323    let contexts = vec![
324        OrderContext {
325            order_id: "ORD-SMALL".to_string(),
326            amount: 50.0,
327            customer_id: "C1".to_string(),
328        },
329        OrderContext {
330            order_id: "ORD-MEDIUM".to_string(),
331            amount: 500.0,
332            customer_id: "C2".to_string(),
333        },
334        OrderContext {
335            order_id: "ORD-LARGE".to_string(),
336            amount: 5000.0,
337            customer_id: "C3".to_string(),
338        },
339    ];
340
341    for context in contexts {
342        let _ = state_machine.fire_event(OrderState::PaymentReceived, OrderEvent::Process, context);
343    }
344}
345
346/// Example 6: With visualization (requires 'visualization' feature)
347#[cfg(feature = "visualization")]
348fn visualization_example() {
349    println!("\n=== Visualization Example ===");
350
351    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
352
353    // Build a complete order flow
354    builder
355        .external_transition()
356        .from(OrderState::New)
357        .to(OrderState::PaymentPending)
358        .on(OrderEvent::Pay)
359        .perform(|_s, _e, _c| {});
360
361    builder
362        .external_transition()
363        .from(OrderState::PaymentPending)
364        .to(OrderState::PaymentReceived)
365        .on(OrderEvent::ConfirmPayment)
366        .perform(|_s, _e, _c| {});
367
368    builder
369        .external_transition()
370        .from(OrderState::PaymentReceived)
371        .to(OrderState::Processing)
372        .on(OrderEvent::Process)
373        .perform(|_s, _e, _c| {});
374
375    builder
376        .external_transition()
377        .from(OrderState::Processing)
378        .to(OrderState::Shipped)
379        .on(OrderEvent::Ship)
380        .perform(|_s, _e, _c| {});
381
382    builder
383        .external_transition()
384        .from(OrderState::Shipped)
385        .to(OrderState::Delivered)
386        .on(OrderEvent::Deliver)
387        .perform(|_s, _e, _c| {});
388
389    builder
390        .external_transitions()
391        .from_among(vec![
392            OrderState::New,
393            OrderState::PaymentPending,
394            OrderState::Processing,
395        ])
396        .to(OrderState::Cancelled)
397        .on(OrderEvent::Cancel)
398        .perform(|_s, _e, _c| {});
399
400    builder
401        .external_transition()
402        .from(OrderState::Cancelled)
403        .to(OrderState::Refunded)
404        .on(OrderEvent::Refund)
405        .perform(|_s, _e, _c| {});
406
407    let state_machine = builder.id("VisualOrderMachine").build();
408
409    println!("DOT Format:");
410    println!("{}", state_machine.to_dot());
411
412    println!("\nPlantUML Format:");
413    println!("{}", state_machine.to_plantuml());
414}
415
416/// Example 7: With parallel regions (requires 'parallel' feature)
417#[cfg(feature = "parallel")]
418fn parallel_example() {
419    println!("\n=== Parallel Regions Example ===");
420
421    // Order processing region
422    let mut order_builder =
423        StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
424    order_builder
425        .external_transition()
426        .from(OrderState::New)
427        .to(OrderState::Processing)
428        .on(OrderEvent::Process)
429        .perform(|_s, _e, ctx| {
430            println!("Order region: Processing order {}", ctx.order_id);
431        });
432
433    // Payment processing region (using same states/events for simplicity)
434    let mut payment_builder =
435        StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
436    payment_builder
437        .external_transition()
438        .from(OrderState::PaymentPending)
439        .to(OrderState::PaymentReceived)
440        .on(OrderEvent::ConfirmPayment)
441        .perform(|_s, _e, ctx| {
442            println!(
443                "Payment region: Payment confirmed for order {}",
444                ctx.order_id
445            );
446        });
447
448    let mut parallel_machine = ParallelStateMachine::new();
449    parallel_machine.add_region(order_builder.id("OrderRegion").build());
450    parallel_machine.add_region(payment_builder.id("PaymentRegion").build());
451
452    let context = OrderContext {
453        order_id: "ORD-PARALLEL".to_string(),
454        amount: 199.99,
455        customer_id: "CUST-P1".to_string(),
456    };
457
458    // Fire events in parallel regions
459    println!("Firing Process event in parallel regions:");
460    let results = parallel_machine.fire_event(
461        vec![OrderState::New, OrderState::PaymentPending],
462        OrderEvent::Process,
463        context.clone(),
464    );
465
466    for (i, result) in results.iter().enumerate() {
467        println!("  Region {}: {:?}", i, result);
468    }
469
470    println!("Firing ConfirmPayment event in parallel regions:");
471    let results = parallel_machine.fire_event(
472        vec![OrderState::Processing, OrderState::PaymentPending],
473        OrderEvent::ConfirmPayment,
474        context,
475    );
476
477    for (i, result) in results.iter().enumerate() {
478        println!("  Region {}: {:?}", i, result);
479    }
480}
481
482/// Example 8: Complete example with multiple features
483#[cfg(all(feature = "history", feature = "metrics", feature = "extended"))]
484fn complete_example() {
485    println!("\n=== Complete Example with Multiple Features ===");
486
487    let mut builder = StateMachineBuilderFactory::create::<OrderState, OrderEvent, OrderContext>();
488
489    // Configure entry/exit actions
490    builder
491        .with_entry_action(OrderState::Processing, |_s, ctx| {
492            println!("[ENTRY] Starting to process order {}", ctx.order_id);
493        })
494        .with_exit_action(OrderState::Processing, |_s, ctx| {
495            println!("[EXIT] Finished processing order {}", ctx.order_id);
496        });
497
498    // Build transitions
499    builder
500        .external_transition()
501        .from(OrderState::New)
502        .to(OrderState::PaymentPending)
503        .on(OrderEvent::Pay)
504        .perform(|_s, _e, ctx| {
505            println!("Payment initiated for ${}", ctx.amount);
506        });
507
508    builder
509        .external_transition()
510        .from(OrderState::PaymentPending)
511        .to(OrderState::PaymentReceived)
512        .on(OrderEvent::ConfirmPayment)
513        .when(|_s, _e, ctx| ctx.amount > 0.0)
514        .perform(|_s, _e, ctx| {
515            println!("Payment confirmed: ${}", ctx.amount);
516        });
517
518    builder
519        .external_transition()
520        .from(OrderState::PaymentReceived)
521        .to(OrderState::Processing)
522        .on(OrderEvent::Process)
523        .perform(|_s, _e, _c| {});
524
525    builder
526        .external_transition()
527        .from(OrderState::Processing)
528        .to(OrderState::Shipped)
529        .on(OrderEvent::Ship)
530        .perform(|_s, _e, ctx| {
531            println!("Order {} shipped", ctx.order_id);
532        });
533
534    builder.set_fail_callback(Arc::new(|state, event, ctx| {
535        println!(
536            "FAILED: Cannot handle {:?} in state {:?} for order {}",
537            event, state, ctx.order_id
538        );
539    }));
540
541    let state_machine = builder.id("CompleteOrderMachine").build();
542
543    // Process an order through the complete flow
544    let context = OrderContext {
545        order_id: "ORD-COMPLETE".to_string(),
546        amount: 999.99,
547        customer_id: "CUST-VIP".to_string(),
548    };
549
550    println!("\nProcessing order through complete flow:");
551    let states = [
552        (OrderState::New, OrderEvent::Pay),
553        (OrderState::PaymentPending, OrderEvent::ConfirmPayment),
554        (OrderState::PaymentReceived, OrderEvent::Process),
555        (OrderState::Processing, OrderEvent::Ship),
556    ];
557
558    for (state, event) in &states {
559        let result = state_machine.fire_event(state.clone(), event.clone(), context.clone());
560        println!("  {:?} + {:?} = {:?}", state, event, result);
561    }
562
563    // Try an invalid transition
564    println!("\nTrying invalid transition:");
565    let _ = state_machine.fire_event(OrderState::Shipped, OrderEvent::Pay, context);
566
567    // Display collected data
568    println!("\nHistory:");
569    for record in state_machine.get_history() {
570        println!(
571            "  {:?} -> {:?} (success: {})",
572            record.from, record.to, record.success
573        );
574    }
575
576    println!("\nMetrics:");
577    let metrics = state_machine.get_metrics();
578    println!(
579        "  Total: {}, Success: {}, Failed: {}",
580        metrics.total_transitions, metrics.successful_transitions, metrics.failed_transitions
581    );
582}
583
584fn main() {
585    basic_example();
586
587    #[cfg(feature = "history")]
588    history_example();
589
590    #[cfg(feature = "extended")]
591    extended_example();
592
593    #[cfg(feature = "metrics")]
594    metrics_example();
595
596    #[cfg(feature = "guards")]
597    guards_example();
598
599    #[cfg(feature = "visualization")]
600    visualization_example();
601
602    #[cfg(feature = "parallel")]
603    parallel_example();
604
605    #[cfg(all(feature = "history", feature = "metrics", feature = "extended"))]
606    complete_example();
607}