151 lines
5.1 KiB
Rust
151 lines
5.1 KiB
Rust
//! # Pattern: Conditional Execution with Data Validation
|
|
//!
|
|
//! This example demonstrates how to implement conditional logic in DAGs by using
|
|
//! simple data transformations rather than traditional Result types.
|
|
//!
|
|
//! ## What You'll Learn
|
|
//! - How to implement validation logic in parallel tasks
|
|
//! - How to use return values for conditional behavior (e.g., 0 for invalid)
|
|
//! - How to categorize and branch based on computed values
|
|
//! - How to aggregate results from multiple conditional branches
|
|
//!
|
|
//! ## Workflow Diagram
|
|
//! ```text
|
|
//! VALIDATE CATEGORIZE SUMMARIZE
|
|
//! ┌─────────────┐ ┌──────────────┐
|
|
//! │ Validate(10)│────20──►│Categorize(25)│────"low"────┐
|
|
//! └─────────────┘ └──────────────┘ │
|
|
//! │
|
|
//! ┌─────────────┐ ┌──────────────┐ │
|
|
//! │ Validate(-5)│────0───►│Categorize(25)│──"invalid"──┼──►┌────────────┐
|
|
//! └─────────────┘ └──────────────┘ │ │ Summarize │
|
|
//! │ └────────────┘
|
|
//! ┌─────────────┐ ┌──────────────┐ │
|
|
//! │ Validate(20)│────40──►│Categorize(25)│────"high"───┘
|
|
//! └─────────────┘ └──────────────┘
|
|
//! ```
|
|
//!
|
|
//! ## Key Concepts
|
|
//! - **Validation as transformation**: Invalid inputs become sentinel values (0)
|
|
//! - **Parallel validation**: All inputs validated simultaneously
|
|
//! - **Data-driven branching**: Categorization based on computed values
|
|
//! - **Type-safe aggregation**: All branches produce the same type (String)
|
|
//!
|
|
//! ## Use Cases
|
|
//! - Input validation pipelines
|
|
//! - Multi-stage data cleaning
|
|
//! - Classification and categorization workflows
|
|
//! - Quality control systems
|
|
//! - Data preprocessing for ML pipelines
|
|
//!
|
|
//! ## Running This Example
|
|
//! ```bash
|
|
//! cargo run --example conditional_workflow
|
|
//! ```
|
|
//!
|
|
//! ## Expected Output
|
|
//! ```text
|
|
//! Processing and categorizing inputs in parallel...
|
|
//!
|
|
//! Valid input 10: doubling to 20
|
|
//! Invalid input -5: returning 0
|
|
//! Valid input 20: doubling to 40
|
|
//!
|
|
//! Final summary: Categories: [low, invalid, high]
|
|
//! Expected: Categories: [low, invalid, high]
|
|
//! ```
|
|
|
|
use dagx::{task, DagResult, DagRunner, Task};
|
|
|
|
// Task that validates and processes input
|
|
struct ValidateAndDouble {
|
|
value: i32,
|
|
}
|
|
|
|
impl ValidateAndDouble {
|
|
fn new(value: i32) -> Self {
|
|
Self { value }
|
|
}
|
|
}
|
|
|
|
#[task]
|
|
impl ValidateAndDouble {
|
|
async fn run(&mut self) -> i32 {
|
|
if self.value > 0 {
|
|
println!("Valid input {}: doubling to {}", self.value, self.value * 2);
|
|
self.value * 2
|
|
} else {
|
|
println!("Invalid input {}: returning 0", self.value);
|
|
0 // Return 0 for invalid inputs
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task that categorizes a number
|
|
struct Categorize {
|
|
threshold: i32,
|
|
}
|
|
|
|
impl Categorize {
|
|
fn new(threshold: i32) -> Self {
|
|
Self { threshold }
|
|
}
|
|
}
|
|
|
|
#[task]
|
|
impl Categorize {
|
|
async fn run(&mut self, value: &i32) -> String {
|
|
if *value == 0 {
|
|
"invalid".to_string()
|
|
} else if *value >= self.threshold {
|
|
"high".to_string()
|
|
} else {
|
|
"low".to_string()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Task that combines categorization results
|
|
struct SummarizeResults;
|
|
|
|
#[task]
|
|
impl SummarizeResults {
|
|
async fn run(cat1: &String, cat2: &String, cat3: &String) -> String {
|
|
format!("Categories: [{}, {}, {}]", cat1, cat2, cat3)
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> DagResult<()> {
|
|
let dag = DagRunner::new();
|
|
|
|
println!("Processing and categorizing inputs in parallel...\n");
|
|
|
|
// Validate and process multiple inputs in parallel
|
|
let processed1 = dag.add_task(ValidateAndDouble::new(10)); // Valid: 20
|
|
let processed2 = dag.add_task(ValidateAndDouble::new(-5)); // Invalid: 0
|
|
let processed3 = dag.add_task(ValidateAndDouble::new(20)); // Valid: 40
|
|
|
|
// Categorize each result (also parallel, threshold = 25)
|
|
let cat1 = dag.add_task(Categorize::new(25)).depends_on(&processed1); // 20 < 25 = "low"
|
|
let cat2 = dag.add_task(Categorize::new(25)).depends_on(&processed2); // 0 = "invalid"
|
|
let cat3 = dag.add_task(Categorize::new(25)).depends_on(&processed3); // 40 >= 25 = "high"
|
|
|
|
// Combine results
|
|
let summary = dag
|
|
.add_task(SummarizeResults)
|
|
.depends_on((&cat1, &cat2, &cat3));
|
|
|
|
// Execute
|
|
dag.run(|fut| {
|
|
tokio::spawn(fut);
|
|
})
|
|
.await?;
|
|
|
|
// Display results
|
|
println!("\nFinal summary: {}", dag.get(summary)?);
|
|
println!("Expected: Categories: [low, invalid, high]");
|
|
|
|
Ok(())
|
|
}
|