Files
dagx/examples/03_fan_in.rs
T

144 lines
4.0 KiB
Rust

//! # Pattern: Fan-In (N→1 Dependencies)
//!
//! This example demonstrates the fan-in pattern where multiple independent source
//! tasks produce values that are aggregated by a single downstream task.
//!
//! ## What You'll Learn
//! - How to create many-to-one task dependencies
//! - How to aggregate results from multiple upstream tasks
//! - How parameter order in `depends_on()` maps to function parameters
//! - How independent tasks run in parallel before aggregation
//!
//! ## Pattern Diagram
//! ```text
//! ┌──────────┐ ┌─────────┐ ┌──────────┐ ┌────────────┐
//! │FetchName │ │FetchAge │ │FetchCity │ │CheckActive │
//! └────┬─────┘ └────┬────┘ └────┬─────┘ └──────┬─────┘
//! │ │ │ │
//! "Alice" 30 "San Francisco" true
//! │ │ │ │
//! └─────────────┴─────────────┴───────────────┘
//! │
//! ┌───────▼────────┐
//! │ BuildProfile │
//! └────────────────┘
//! │
//! "User Profile..."
//! ```
//!
//! ## Key Concepts
//! - **Fan-in**: Multiple tasks' outputs are consumed by a single downstream task
//! - **Aggregation**: Combining multiple values into a single result
//! - **Parameter order**: The order in `.depends_on((&a, &b, &c))` must match
//! the function signature `fn run(a: &T1, b: &T2, c: &T3)`
//!
//! ## Use Cases
//! - Aggregating data from multiple API endpoints
//! - Combining results from parallel computations
//! - Building composite objects from multiple sources
//! - Waiting for multiple prerequisites before proceeding
//!
//! ## Running This Example
//! ```bash
//! cargo run --example fan_in
//! ```
//!
//! ## Expected Output
//! ```text
//! Running fan-in DAG...
//!
//! Fetching name...
//! Fetching age...
//! Fetching city...
//! Checking active status...
//!
//! Building user profile...
//!
//! User Profile:
//! Name: Alice
//! Age: 30
//! City: San Francisco
//! Active: true
//! ```
use dagx::{task, DagRunner};
use futures::FutureExt;
struct FetchName;
#[task]
impl FetchName {
async fn run() -> String {
println!("Fetching name...");
"Alice".to_string()
}
}
struct FetchAge;
#[task]
impl FetchAge {
async fn run() -> i32 {
println!("Fetching age...");
30
}
}
struct FetchCity;
#[task]
impl FetchCity {
async fn run() -> String {
println!("Fetching city...");
"San Francisco".to_string()
}
}
struct CheckActive;
#[task]
impl CheckActive {
async fn run() -> bool {
println!("Checking active status...");
true
}
}
struct BuildProfile;
#[task]
impl BuildProfile {
async fn run(name: &String, age: &i32, city: &String, active: &bool) -> String {
println!("\nBuilding user profile...");
format!(
"User Profile:\n Name: {}\n Age: {}\n City: {}\n Active: {}",
name, age, city, active
)
}
}
#[tokio::main]
async fn main() {
let dag = DagRunner::new();
// Multiple independent source tasks
let name = dag.add_task(FetchName);
let age = dag.add_task(FetchAge);
let city = dag.add_task(FetchCity);
let active = dag.add_task(CheckActive);
// Single downstream task consuming all values
let profile = dag
.add_task(BuildProfile)
.depends_on((&name, &age, &city, &active));
// Run the DAG
println!("Running fan-in DAG...\n");
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
.unwrap();
// Print result
println!("\n{}", dag.get(profile).unwrap());
}