1use graph_sp::Graph;
5use std::collections::HashMap;
6
7fn 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#[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 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
59fn 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
74fn 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
89fn 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
118fn main() {
123 println!("═══════════════════════════════════════════════════════════");
124 println!(" Variant Pattern Demo (sigexec-style)");
125 println!(" Full Actual Syntax Examples");
126 println!("═══════════════════════════════════════════════════════════\n");
127
128 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 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 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 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 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}