//! Basic workflow example demonstrating orchestrator usage use orchestrator::{ interface::LocalInterface, orchestrator::Orchestrator, OrchestratedFlow, OrchestratedFlowStep, FlowStatus, }; use std::sync::Arc; use std::collections::HashMap; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize logging tracing_subscriber::fmt().init(); // Create executor let executor = Arc::new(LocalInterface::new()); // Create orchestrator let orchestrator = Orchestrator::new(executor); println!("πŸš€ Starting basic workflow example"); // Example 1: Simple sequential workflow println!("\nπŸ“‹ Example 1: Sequential Workflow"); let sequential_flow = create_sequential_workflow(); let flow_id = orchestrator.execute_flow(sequential_flow).await?; // Wait for completion and show results wait_and_show_results(&orchestrator, flow_id, "Sequential").await; // Example 2: Parallel workflow with convergence println!("\nπŸ“‹ Example 2: Parallel Workflow"); let parallel_flow = create_parallel_workflow(); let flow_id = orchestrator.execute_flow(parallel_flow).await?; // Wait for completion and show results wait_and_show_results(&orchestrator, flow_id, "Parallel").await; // Example 3: Complex workflow with multiple dependencies println!("\nπŸ“‹ Example 3: Complex Workflow"); let complex_flow = create_complex_workflow(); let flow_id = orchestrator.execute_flow(complex_flow).await?; // Wait for completion and show results wait_and_show_results(&orchestrator, flow_id, "Complex").await; // Clean up completed flows orchestrator.cleanup_completed_flows().await; println!("\nβœ… All examples completed successfully!"); Ok(()) } /// Create a simple sequential workflow fn create_sequential_workflow() -> OrchestratedFlow { let step1 = OrchestratedFlowStep::new("data_preparation") .script(r#" let data = [1, 2, 3, 4, 5]; let sum = 0; for item in data { sum += item; } let result = sum; "#) .context_id("sequential_context") .worker_id("worker_1"); let step2 = OrchestratedFlowStep::new("data_processing") .script(r#" let processed_data = dep_1_result * 2; let result = processed_data; "#) .depends_on(step1.id()) .context_id("sequential_context") .worker_id("worker_2"); let step3 = OrchestratedFlowStep::new("data_output") .script(r#" let final_result = "Processed value: " + dep_2_result; let result = final_result; "#) .depends_on(step2.id()) .context_id("sequential_context") .worker_id("worker_3"); OrchestratedFlow::new("sequential_workflow") .add_step(step1) .add_step(step2) .add_step(step3) } /// Create a parallel workflow with convergence fn create_parallel_workflow() -> OrchestratedFlow { let step1 = OrchestratedFlowStep::new("fetch_user_data") .script(r#" let user_id = 12345; let user_name = "Alice"; let result = user_name; "#) .context_id("parallel_context") .worker_id("user_service"); let step2 = OrchestratedFlowStep::new("fetch_order_data") .script(r#" let order_id = 67890; let order_total = 99.99; let result = order_total; "#) .context_id("parallel_context") .worker_id("order_service"); let step3 = OrchestratedFlowStep::new("fetch_inventory_data") .script(r#" let product_id = "ABC123"; let stock_count = 42; let result = stock_count; "#) .context_id("parallel_context") .worker_id("inventory_service"); let step4 = OrchestratedFlowStep::new("generate_report") .script(r#" let report = "User: " + dep_1_result + ", Order Total: $" + dep_2_result + ", Stock: " + dep_3_result + " units"; let result = report; "#) .depends_on(step1.id()) .depends_on(step2.id()) .depends_on(step3.id()) .context_id("parallel_context") .worker_id("report_service"); OrchestratedFlow::new("parallel_workflow") .add_step(step1) .add_step(step2) .add_step(step3) .add_step(step4) } /// Create a complex workflow with multiple dependency levels fn create_complex_workflow() -> OrchestratedFlow { // Level 1: Initial data gathering let step1 = OrchestratedFlowStep::new("load_config") .script(r#" let config = #{ api_url: "https://api.example.com", timeout: 30, retries: 3 }; let result = config.api_url; "#) .context_id("complex_context") .worker_id("config_service"); let step2 = OrchestratedFlowStep::new("authenticate") .script(r#" let token = "auth_token_12345"; let expires_in = 3600; let result = token; "#) .context_id("complex_context") .worker_id("auth_service"); // Level 2: Data fetching (depends on config and auth) let step3 = OrchestratedFlowStep::new("fetch_customers") .script(r#" let api_url = dep_1_result; let auth_token = dep_2_result; let customers = ["Customer A", "Customer B", "Customer C"]; let result = customers.len(); "#) .depends_on(step1.id()) .depends_on(step2.id()) .context_id("complex_context") .worker_id("customer_service"); let step4 = OrchestratedFlowStep::new("fetch_products") .script(r#" let api_url = dep_1_result; let auth_token = dep_2_result; let products = ["Product X", "Product Y", "Product Z"]; let result = products.len(); "#) .depends_on(step1.id()) .depends_on(step2.id()) .context_id("complex_context") .worker_id("product_service"); // Level 3: Data processing (depends on fetched data) let step5 = OrchestratedFlowStep::new("calculate_metrics") .script(r#" let customer_count = dep_3_result; let product_count = dep_4_result; let ratio = customer_count / product_count; let result = ratio; "#) .depends_on(step3.id()) .depends_on(step4.id()) .context_id("complex_context") .worker_id("analytics_service"); // Level 4: Final reporting let step6 = OrchestratedFlowStep::new("generate_dashboard") .script(r#" let customer_count = dep_3_result; let product_count = dep_4_result; let ratio = dep_5_result; let dashboard = "Dashboard: " + customer_count + " customers, " + product_count + " products, ratio: " + ratio; let result = dashboard; "#) .depends_on(step3.id()) .depends_on(step4.id()) .depends_on(step5.id()) .context_id("complex_context") .worker_id("dashboard_service"); OrchestratedFlow::new("complex_workflow") .add_step(step1) .add_step(step2) .add_step(step3) .add_step(step4) .add_step(step5) .add_step(step6) } /// Wait for flow completion and show results async fn wait_and_show_results( orchestrator: &Orchestrator, flow_id: u32, workflow_name: &str, ) { println!(" ⏳ Executing {} workflow (ID: {})...", workflow_name, flow_id); // Poll for completion loop { tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; if let Some(execution) = orchestrator.get_flow_status(flow_id).await { match execution.status { FlowStatus::Completed => { println!(" βœ… {} workflow completed successfully!", workflow_name); println!(" πŸ“Š Executed {} steps in {:?}", execution.completed_steps.len(), execution.completed_at.unwrap() - execution.started_at); // Show step results for (step_id, outputs) in &execution.step_results { if let Some(result) = outputs.get("result") { let step_name = execution.flow.orchestrated_steps .iter() .find(|s| s.id() == *step_id) .map(|s| s.flow_step.name.as_str()) .unwrap_or("unknown"); println!(" πŸ“ Step '{}': {}", step_name, result); } } break; } FlowStatus::Failed => { println!(" ❌ {} workflow failed!", workflow_name); if !execution.failed_steps.is_empty() { println!(" πŸ’₯ Failed steps: {:?}", execution.failed_steps); } break; } FlowStatus::Running => { print!("."); std::io::Write::flush(&mut std::io::stdout()).unwrap(); } FlowStatus::Pending => { println!(" ⏸️ {} workflow is pending...", workflow_name); } } } else { println!(" ❓ {} workflow not found!", workflow_name); break; } } }