cycle_detection/
cycle_detection.rs

1//! Cycle detection example: Demonstrating error handling for circular dependencies
2//!
3//! This example shows:
4//! - What happens when you create circular dependencies
5//! - How the system detects and reports cycles
6
7use zzstat::source::ConstantSource;
8use zzstat::transform::ScalingTransform;
9use zzstat::*;
10
11fn main() {
12    let mut resolver = StatResolver::new();
13
14    // Create stats that depend on each other in a cycle
15    let a_id = StatId::from_str("A");
16    let b_id = StatId::from_str("B");
17    let c_id = StatId::from_str("C");
18
19    println!("=== Setting up circular dependencies ===\n");
20
21    // A depends on B
22    resolver.register_source(a_id.clone(), Box::new(ConstantSource(10.0)));
23    resolver.register_transform(
24        a_id.clone(),
25        Box::new(ScalingTransform::new(b_id.clone(), 1.0)),
26    );
27    println!("A: 10 (base) + B * 1.0");
28
29    // B depends on C
30    resolver.register_source(b_id.clone(), Box::new(ConstantSource(20.0)));
31    resolver.register_transform(
32        b_id.clone(),
33        Box::new(ScalingTransform::new(c_id.clone(), 1.0)),
34    );
35    println!("B: 20 (base) + C * 1.0");
36
37    // C depends on A (creates cycle: A -> B -> C -> A)
38    resolver.register_source(c_id.clone(), Box::new(ConstantSource(30.0)));
39    resolver.register_transform(
40        c_id.clone(),
41        Box::new(ScalingTransform::new(a_id.clone(), 1.0)),
42    );
43    println!("C: 30 (base) + A * 1.0");
44
45    println!("\n=== Attempting to resolve (should detect cycle) ===\n");
46
47    let context = StatContext::new();
48    match resolver.resolve(&a_id, &context) {
49        Err(StatError::CycleDetected(cycle)) => {
50            println!("āœ“ Cycle detected successfully!");
51            println!("\nCycle path:");
52            for (i, stat_id) in cycle.iter().enumerate() {
53                if i < cycle.len() - 1 {
54                    print!("{} -> ", stat_id);
55                } else {
56                    println!("{}", stat_id);
57                }
58            }
59        }
60        Err(e) => {
61            println!("āœ— Unexpected error: {}", e);
62        }
63        Ok(_) => {
64            println!("āœ— ERROR: Cycle was not detected! This should not happen.");
65        }
66    }
67
68    println!("\n=== Valid dependency chain (no cycle) ===\n");
69
70    // Reset resolver
71    let mut resolver2 = StatResolver::new();
72
73    let x_id = StatId::from_str("X");
74    let y_id = StatId::from_str("Y");
75    let z_id = StatId::from_str("Z");
76
77    // Linear chain: X -> Y -> Z (no cycle)
78    resolver2.register_source(x_id.clone(), Box::new(ConstantSource(10.0)));
79    println!("X: 10 (base)");
80
81    resolver2.register_source(y_id.clone(), Box::new(ConstantSource(20.0)));
82    resolver2.register_transform(
83        y_id.clone(),
84        Box::new(ScalingTransform::new(x_id.clone(), 1.0)),
85    );
86    println!("Y: 20 (base) + X * 1.0");
87
88    resolver2.register_source(z_id.clone(), Box::new(ConstantSource(30.0)));
89    resolver2.register_transform(
90        z_id.clone(),
91        Box::new(ScalingTransform::new(y_id.clone(), 1.0)),
92    );
93    println!("Z: 30 (base) + Y * 1.0");
94
95    println!("\n=== Resolving valid chain ===\n");
96
97    let results = resolver2.resolve_all(&context).unwrap();
98
99    println!("Results:");
100    println!("  X: {:.2}", results[&x_id].value);
101    println!("  Y: {:.2} (20 + 10)", results[&y_id].value);
102    println!("  Z: {:.2} (30 + 30)", results[&z_id].value);
103
104    println!("\nāœ“ Valid dependency chain resolved successfully!");
105}