variant_demo_full/
variant_demo_full.rs

1// Comprehensive demonstration of the variant pattern (sigexec-style)
2// Shows the full actual syntax with working examples
3
4use graph_sp::Graph;
5use std::collections::HashMap;
6
7// =============================================================================
8// Example 1: Basic Variant with Factory Function
9// =============================================================================
10
11/// Factory function that creates a scaler for a specific factor
12/// This is the key pattern: factory takes a parameter, returns a closure
13fn make_scaler(factor: f64) -> impl Fn(&HashMap<String, String>, &HashMap<String, String>) -> HashMap<String, String> {
14    move |inputs, _variant_params| {
15        let value = inputs.get("input_data").unwrap().parse::<f64>().unwrap();
16        let scaled = value * factor;
17        
18        let mut outputs = HashMap::new();
19        outputs.insert("scaled_value".to_string(), scaled.to_string());
20        outputs.insert("factor_used".to_string(), factor.to_string());
21        outputs
22    }
23}
24
25// =============================================================================
26// Example 2: Filter Factory with Multiple Parameters
27// =============================================================================
28
29#[derive(Clone)]
30struct FilterConfig {
31    cutoff: f64,
32    mode: String,
33}
34
35impl std::fmt::Display for FilterConfig {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(f, "{}_cutoff{}", self.mode, self.cutoff)
38    }
39}
40
41fn make_filter(config: FilterConfig) -> impl Fn(&HashMap<String, String>, &HashMap<String, String>) -> HashMap<String, String> {
42    move |inputs, _variant_params| {
43        let value = inputs.get("data").unwrap().parse::<f64>().unwrap();
44        
45        // Apply filtering based on config
46        let filtered = match config.mode.as_str() {
47            "lowpass" => value * config.cutoff,
48            "highpass" => value * (1.0 - config.cutoff),
49            _ => value,
50        };
51        
52        let mut outputs = HashMap::new();
53        outputs.insert("filtered".to_string(), filtered.to_string());
54        outputs.insert("filter_mode".to_string(), config.mode.clone());
55        outputs
56    }
57}
58
59// =============================================================================
60// Example 3: Offset Factory (Simple Parameter Sweep)
61// =============================================================================
62
63fn make_offsetter(offset: i32) -> impl Fn(&HashMap<String, String>, &HashMap<String, String>) -> HashMap<String, String> {
64    move |inputs, _variant_params| {
65        let value = inputs.get("number").unwrap().parse::<i32>().unwrap();
66        let result = value + offset;
67        
68        let mut outputs = HashMap::new();
69        outputs.insert("offset_result".to_string(), result.to_string());
70        outputs
71    }
72}
73
74// =============================================================================
75// Example 4: String Processor Factory
76// =============================================================================
77
78fn make_processor(prefix: &'static str) -> impl Fn(&HashMap<String, String>, &HashMap<String, String>) -> HashMap<String, String> {
79    move |inputs, _variant_params| {
80        let text = inputs.get("text").unwrap();
81        let processed = format!("[{}] {}", prefix, text);
82        
83        let mut outputs = HashMap::new();
84        outputs.insert("processed_text".to_string(), processed);
85        outputs
86    }
87}
88
89// =============================================================================
90// Helper Functions for Demonstrations
91// =============================================================================
92
93fn data_source(_inputs: &HashMap<String, String>, _variant_params: &HashMap<String, String>) -> HashMap<String, String> {
94    let mut outputs = HashMap::new();
95    outputs.insert("value".to_string(), "10.0".to_string());
96    outputs
97}
98
99fn number_source(_inputs: &HashMap<String, String>, _variant_params: &HashMap<String, String>) -> HashMap<String, String> {
100    let mut outputs = HashMap::new();
101    outputs.insert("num".to_string(), "42".to_string());
102    outputs
103}
104
105fn text_source(_inputs: &HashMap<String, String>, _variant_params: &HashMap<String, String>) -> HashMap<String, String> {
106    let mut outputs = HashMap::new();
107    outputs.insert("message".to_string(), "Hello World".to_string());
108    outputs
109}
110
111fn stats_node(inputs: &HashMap<String, String>, _variant_params: &HashMap<String, String>) -> HashMap<String, String> {
112    let scaled = inputs.get("result").unwrap();
113    let mut outputs = HashMap::new();
114    outputs.insert("summary".to_string(), format!("Result: {}", scaled));
115    outputs
116}
117
118// =============================================================================
119// MAIN DEMONSTRATION
120// =============================================================================
121
122fn main() {
123    println!("═══════════════════════════════════════════════════════════");
124    println!("  Variant Pattern Demo (sigexec-style)");
125    println!("  Full Actual Syntax Examples");
126    println!("═══════════════════════════════════════════════════════════\n");
127
128    // =========================================================================
129    // Demo 1: Single Variant - Basic Factory Pattern
130    // =========================================================================
131    println!("Demo 1: Single Variant with Factory Function");
132    println!("─────────────────────────────────────────────────────────\n");
133    
134    println!("📝 Code:");
135    println!("```rust");
136    println!("fn make_scaler(factor: f64) -> impl Fn(...) -> ... {{");
137    println!("    move |inputs, _| {{");
138    println!("        let value = inputs.get(\"input_data\").unwrap().parse::<f64>().unwrap();");
139    println!("        let scaled = value * factor;");
140    println!("        outputs.insert(\"scaled_value\", scaled.to_string());");
141    println!("    }}");
142    println!("}}");
143    println!();
144    println!("let mut graph = Graph::new();");
145    println!("graph.add(data_source, Some(\"Source\"), None, Some(vec![(\"value\", \"data\")]));");
146    println!("graph.variant(");
147    println!("    make_scaler,              // Factory function");
148    println!("    vec![2.0, 3.0, 5.0],      // Parameter values to sweep");
149    println!("    Some(\"Scale\"),            // Label");
150    println!("    Some(vec![(\"data\", \"input_data\")]),  // Input mapping");
151    println!("    Some(vec![(\"scaled_value\", \"result\")])  // Output mapping");
152    println!(");");
153    println!("```\n");
154
155    let mut graph1 = Graph::new();
156    graph1.add(
157        data_source,
158        Some("Source"),
159        None,
160        Some(vec![("value", "data")])
161    );
162    graph1.variant(
163        make_scaler,
164        vec![2.0, 3.0, 5.0],
165        Some("Scale"),
166        Some(vec![("data", "input_data")]),
167        Some(vec![("scaled_value", "result")])
168    );
169    
170    let dag1 = graph1.build();
171    println!("🎯 What happens:");
172    println!("  • Factory creates 3 nodes: Scale_2.0, Scale_3.0, Scale_5.0");
173    println!("  • Each node multiplies input by its factor");
174    println!("  • All variants can execute in parallel");
175    println!();
176    
177    let stats1 = dag1.stats();
178    println!("📈 DAG Statistics:");
179    println!("  - Total nodes: {}", stats1.node_count);
180    println!("  - Depth: {} levels", stats1.depth);
181    println!("  - Max parallelism: {} nodes can run simultaneously", stats1.max_parallelism);
182    println!();
183    
184    println!("🔍 Mermaid Visualization:");
185    println!("{}", dag1.to_mermaid());
186    println!();
187
188    // =========================================================================
189    // Demo 2: Multiple Variants - Cartesian Product
190    // =========================================================================
191    println!("\nDemo 2: Multiple Variants (Cartesian Product)");
192    println!("─────────────────────────────────────────────────────────\n");
193    
194    println!("📝 Code:");
195    println!("```rust");
196    println!("graph.add(data_source, Some(\"Generate\"), None, Some(vec![(\"value\", \"data\")]));");
197    println!("graph.variant(make_scaler, vec![2.0, 3.0], Some(\"Scale\"), ...);");
198    println!("graph.variant(make_offsetter, vec![10, 20], Some(\"Offset\"), ...);");
199    println!("graph.add(stats_node, Some(\"Stats\"), Some(vec![(\"result\", \"result\")]), None);");
200    println!("```\n");
201    
202    let mut graph2 = Graph::new();
203    graph2.add(
204        data_source,
205        Some("Generate"),
206        None,
207        Some(vec![("value", "data")])
208    );
209    graph2.variant(
210        make_scaler,
211        vec![2.0, 3.0],
212        Some("Scale"),
213        Some(vec![("data", "input_data")]),
214        Some(vec![("scaled_value", "result")])
215    );
216    graph2.variant(
217        make_offsetter,
218        vec![10, 20],
219        Some("Offset"),
220        Some(vec![("result", "number")]),
221        Some(vec![("offset_result", "result")])
222    );
223    graph2.add(
224        stats_node,
225        Some("Stats"),
226        Some(vec![("result", "result")]),
227        Some(vec![("summary", "final")])
228    );
229    
230    let dag2 = graph2.build();
231    println!("🎯 What happens:");
232    println!("  • Scale creates 2 variants: x2.0, x3.0");
233    println!("  • Offset creates 2 variants: +10, +20");
234    println!("  • Total combinations: 2 × 2 = 4 execution paths");
235    println!("  • Each path: Generate → Scale[variant] → Offset[variant] → Stats");
236    println!();
237    
238    let stats2 = dag2.stats();
239    println!("📈 DAG Statistics:");
240    println!("  - Total nodes: {}", stats2.node_count);
241    println!("  - Depth: {} levels", stats2.depth);
242    println!("  - Execution paths: 4 (2 scales × 2 offsets)");
243    println!();
244    
245    println!("🔍 Mermaid Visualization:");
246    println!("{}", dag2.to_mermaid());
247    println!();
248
249    // =========================================================================
250    // Demo 3: Complex Factory - Struct Configuration
251    // =========================================================================
252    println!("\nDemo 3: Complex Factory with Struct Configuration");
253    println!("─────────────────────────────────────────────────────────\n");
254    
255    println!("📝 Code:");
256    println!("```rust");
257    println!("#[derive(Clone)]");
258    println!("struct FilterConfig {{");
259    println!("    cutoff: f64,");
260    println!("    mode: String,");
261    println!("}}");
262    println!();
263    println!("fn make_filter(config: FilterConfig) -> impl Fn(...) -> ... {{");
264    println!("    move |inputs, _| {{");
265    println!("        let value = inputs.get(\"data\").unwrap().parse::<f64>().unwrap();");
266    println!("        let filtered = match config.mode.as_str() {{");
267    println!("            \"lowpass\" => value * config.cutoff,");
268    println!("            \"highpass\" => value * (1.0 - config.cutoff),");
269    println!("            _ => value,");
270    println!("        }};");
271    println!("    }}");
272    println!("}}");
273    println!();
274    println!("let configs = vec![");
275    println!("    FilterConfig {{ cutoff: 0.5, mode: \"lowpass\".to_string() }},");
276    println!("    FilterConfig {{ cutoff: 0.3, mode: \"highpass\".to_string() }},");
277    println!("    FilterConfig {{ cutoff: 0.7, mode: \"lowpass\".to_string() }},");
278    println!("];");
279    println!("graph.variant(make_filter, configs, Some(\"Filter\"), ...);");
280    println!("```\n");
281
282    let configs = vec![
283        FilterConfig { cutoff: 0.5, mode: "lowpass".to_string() },
284        FilterConfig { cutoff: 0.3, mode: "highpass".to_string() },
285        FilterConfig { cutoff: 0.7, mode: "lowpass".to_string() },
286    ];
287    
288    let mut graph3 = Graph::new();
289    graph3.add(
290        data_source,
291        Some("Source"),
292        None,
293        Some(vec![("value", "data")])
294    );
295    graph3.variant(
296        make_filter,
297        configs,
298        Some("Filter"),
299        Some(vec![("data", "data")]),
300        Some(vec![("filtered", "result")])
301    );
302    
303    let dag3 = graph3.build();
304    println!("🎯 What happens:");
305    println!("  • 3 filter variants created with different configurations");
306    println!("  • Each variant uses its own FilterConfig struct");
307    println!("  • Demonstrates passing complex types to factory");
308    println!();
309    
310    let stats3 = dag3.stats();
311    println!("📈 DAG Statistics:");
312    println!("  - Total nodes: {}", stats3.node_count);
313    println!("  - Filter variants: 3");
314    println!("  - Max parallelism: {} nodes", stats3.max_parallelism);
315    println!();
316
317    // =========================================================================
318    // Demo 4: String Processing Variants
319    // =========================================================================
320    println!("\nDemo 4: String Processing Variants");
321    println!("─────────────────────────────────────────────────────────\n");
322    
323    println!("📝 Code:");
324    println!("```rust");
325    println!("fn make_processor(prefix: &'static str) -> impl Fn(...) -> ... {{");
326    println!("    move |inputs, _| {{");
327    println!("        let text = inputs.get(\"text\").unwrap();");
328    println!("        let processed = format!(\"[{{}}] {{}}\", prefix, text);");
329    println!("        outputs.insert(\"processed_text\", processed);");
330    println!("    }}");
331    println!("}}");
332    println!();
333    println!("graph.variant(");
334    println!("    make_processor,");
335    println!("    vec![\"INFO\", \"WARN\", \"ERROR\"],");
336    println!("    Some(\"LogLevel\"),");
337    println!("    Some(vec![(\"message\", \"text\")]),");
338    println!("    Some(vec![(\"processed_text\", \"log\")])");
339    println!(");");
340    println!("```\n");
341
342    let mut graph4 = Graph::new();
343    graph4.add(
344        text_source,
345        Some("Source"),
346        None,
347        Some(vec![("message", "message")])
348    );
349    graph4.variant(
350        make_processor,
351        vec!["INFO", "WARN", "ERROR"],
352        Some("LogLevel"),
353        Some(vec![("message", "text")]),
354        Some(vec![("processed_text", "log")])
355    );
356    
357    let dag4 = graph4.build();
358    println!("🎯 What happens:");
359    println!("  • 3 log level variants: INFO, WARN, ERROR");
360    println!("  • Each prefixes the message with its log level");
361    println!("  • Demonstrates string/static str parameters");
362    println!();
363    
364    let stats4 = dag4.stats();
365    println!("📈 DAG Statistics:");
366    println!("  - Total nodes: {}", stats4.node_count);
367    println!("  - Log variants: 3");
368    println!();
369    
370    println!("🔍 Mermaid Visualization:");
371    println!("{}", dag4.to_mermaid());
372    println!();
373
374    // =========================================================================
375    // Summary
376    // =========================================================================
377    println!("\n═══════════════════════════════════════════════════════════");
378    println!("  Summary: Key Variant Pattern Features");
379    println!("═══════════════════════════════════════════════════════════\n");
380    
381    println!("✅ Factory Function Pattern:");
382    println!("   • Factory takes parameter(s), returns closure");
383    println!("   • Closure captures parameters in its environment");
384    println!("   • Same signature as regular node functions");
385    println!();
386    
387    println!("✅ Parameter Flexibility:");
388    println!("   • Primitives: f64, i32, &str");
389    println!("   • Structs: Custom configuration objects");
390    println!("   • Arrays/Vectors: Multiple values at once");
391    println!();
392    
393    println!("✅ Cartesian Products:");
394    println!("   • Multiple .variant() calls create all combinations");
395    println!("   • Example: 2 scales × 3 filters = 6 execution paths");
396    println!();
397    
398    println!("✅ Port Mapping:");
399    println!("   • Variants use same tuple-based syntax");
400    println!("   • (broadcast_var, impl_var) for inputs");
401    println!("   • (impl_var, broadcast_var) for outputs");
402    println!();
403    
404    println!("✅ Parallel Execution:");
405    println!("   • All variants at same level can run in parallel");
406    println!("   • DAG analysis identifies parallelization opportunities");
407    println!();
408}