use ops_rs::batch;
use ops_rs::prelude::*;
use serde_json::json;
struct ValidationOp;
#[async_trait]
impl Op<String> for ValidationOp {
async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<String> {
let input = dry.get_required::<String>("input")?;
if input.is_empty() {
return Err(OpError::ExecutionFailed(
"Input cannot be empty".to_string(),
));
}
dry.insert("validated_input", input.clone());
Ok(format!("Validated: {}", input))
}
fn metadata(&self) -> OpMetadata {
OpMetadata::builder("ValidationOp")
.description("Validates input is not empty")
.input_schema(json!({
"type": "object",
"properties": {
"input": { "type": "string" }
},
"required": ["input"]
}))
.output_schema(json!({
"type": "object",
"properties": {
"validated_input": { "type": "string" }
}
}))
.build()
}
}
struct TransformOp;
#[async_trait]
impl Op<String> for TransformOp {
async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<String> {
let input = dry.get_required::<String>("validated_input")?;
let transformed = input.to_uppercase();
dry.insert("transformed_input", transformed.clone());
Ok(format!("Transformed: {}", transformed))
}
fn metadata(&self) -> OpMetadata {
OpMetadata::builder("TransformOp")
.description("Transforms input to uppercase")
.input_schema(json!({
"type": "object",
"properties": {
"validated_input": { "type": "string" }
},
"required": ["validated_input"]
}))
.output_schema(json!({
"type": "object",
"properties": {
"transformed_input": { "type": "string" }
}
}))
.build()
}
}
struct PersistOp;
#[async_trait]
impl Op<String> for PersistOp {
async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<String> {
let input = dry.get_required::<String>("transformed_input")?;
println!("Persisting: {}", input);
dry.insert("final_result", input.clone());
Ok(format!("Persisted: {}", input))
}
fn metadata(&self) -> OpMetadata {
OpMetadata::builder("PersistOp")
.description("Persists the transformed data")
.input_schema(json!({
"type": "object",
"properties": {
"transformed_input": { "type": "string" }
},
"required": ["transformed_input"]
}))
.output_schema(json!({
"type": "object",
"properties": {
"final_result": { "type": "string" }
}
}))
.build()
}
}
batch! {
ProcessingPipeline<String> = [
ValidationOp,
TransformOp,
PersistOp
]
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Batch Macro Demo ===\n");
let mut dry = DryContext::new().with_value("input", "hello world");
let mut wet = WetContext::new();
let pipeline = ProcessingPipeline::new();
let metadata = pipeline.metadata();
println!("Pipeline metadata:");
println!(" Name: {}", metadata.name);
println!(
" Description: {}",
metadata.description.unwrap_or_default()
);
if let Some(input_schema) = &metadata.input_schema {
if let Some(required) = input_schema.get("required").and_then(|r| r.as_array()) {
println!(" Required inputs: {:?}", required);
}
}
println!("\nExecuting pipeline...\n");
let results = pipeline.perform(&mut dry, &mut wet).await?;
println!("Pipeline results:");
for (i, result) in results.iter().enumerate() {
println!(" Op {}: {}", i + 1, result);
}
println!("\nFinal context state:");
println!(" final_result: {:?}", dry.get::<String>("final_result"));
println!("\n--- Testing with empty input (will fail validation) ---\n");
let mut dry_fail = DryContext::new().with_value("input", ""); let mut wet_fail = WetContext::new();
let pipeline_continue = ProcessingPipeline::new().with_continue_on_error(true);
match pipeline_continue
.perform(&mut dry_fail, &mut wet_fail)
.await
{
Ok(results) => {
println!("Results with continue_on_error: {:?}", results);
}
Err(e) => {
println!("Pipeline failed: {}", e);
}
}
Ok(())
}