ops-rs 1.63.594

A Rust ops framework with composable wrappers and batch execution
Documentation
use ops::dry_put;
use ops::prelude::*;
use std::sync::{Arc, Mutex};

/// Example demonstrating the repeat! macro with rollback functionality

struct CounterOp {
    counter: Arc<Mutex<Vec<u32>>>,
}

impl CounterOp {
    fn new(counter: Arc<Mutex<Vec<u32>>>) -> Self {
        Self { counter }
    }
}

#[async_trait]
impl Op<u32> for CounterOp {
    async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<u32> {
        let iteration: usize = dry.get("iteration_counter").unwrap_or(0);
        let value = (iteration + 1) as u32;

        self.counter.lock().unwrap().push(value);
        println!(
            "✓ CounterOp performed for iteration {}: value = {}",
            iteration, value
        );
        Ok(value)
    }

    async fn rollback(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<()> {
        let iteration: usize = dry.get("iteration_counter").unwrap_or(0);
        let value = (iteration + 1) as u32;

        let mut counter = self.counter.lock().unwrap();
        if let Some(pos) = counter.iter().position(|&x| x == value) {
            counter.remove(pos);
            println!(
                "↶ CounterOp rolled back for iteration {}: removed value = {}",
                iteration, value
            );
        }
        Ok(())
    }

    fn metadata(&self) -> OpMetadata {
        OpMetadata::builder("CounterOp").build()
    }
}

struct FailingOp {
    fail_on_iteration: usize,
}

impl FailingOp {
    fn new(fail_on_iteration: usize) -> Self {
        Self { fail_on_iteration }
    }
}

#[async_trait]
impl Op<u32> for FailingOp {
    async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<u32> {
        let iteration: usize = dry.get("iteration_counter").unwrap_or(0);

        if iteration == self.fail_on_iteration {
            println!("✗ FailingOp failed on iteration {}", iteration);
            return Err(OpError::ExecutionFailed("Intentional failure".to_string()));
        }

        println!("✓ FailingOp succeeded on iteration {}", iteration);
        Ok(iteration as u32)
    }

    fn metadata(&self) -> OpMetadata {
        OpMetadata::builder("FailingOp").build()
    }
}

// Define a repeat operation using the macro
// Note: We'll need to create a custom version since the macro doesn't support
// passing runtime variables to the ops. Let's demonstrate with a simpler approach.

struct TestLoop {
    counter_var: String,
    limit_var: String,
    shared_counter: Arc<Mutex<Vec<u32>>>,
}

impl TestLoop {
    fn new(shared_counter: Arc<Mutex<Vec<u32>>>) -> Self {
        Self {
            counter_var: "iteration_counter".to_string(),
            limit_var: "max_iterations".to_string(),
            shared_counter,
        }
    }
}

#[async_trait]
impl Op<Vec<u32>> for TestLoop {
    async fn perform(&self, dry: &mut DryContext, wet: &mut WetContext) -> OpResult<Vec<u32>> {
        let limit = dry.get_required::<usize>(&self.limit_var)?;

        let ops: Vec<Arc<dyn Op<u32>>> = vec![
            Arc::new(CounterOp::new(self.shared_counter.clone())),
            Arc::new(FailingOp::new(2)), // Fail on iteration 2
        ];

        let loop_op = ops::LoopOp::new(self.counter_var.clone(), limit, ops);
        loop_op.perform(dry, wet).await
    }

    fn metadata(&self) -> OpMetadata {
        OpMetadata::builder("TestLoop").build()
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Repeat Macro Rollback Example ===\n");

    let counter = Arc::new(Mutex::new(Vec::new()));

    // Create the loop operation
    let test_loop = TestLoop::new(counter.clone());

    let mut dry = DryContext::new();
    let mut wet = WetContext::new();

    // Set the limit in the dry context
    let max_iterations = 4usize;
    dry_put!(dry, max_iterations);

    println!("Initial counter state: {:?}", *counter.lock().unwrap());
    println!("Running loop with {} iterations...\n", max_iterations);

    match test_loop.perform(&mut dry, &mut wet).await {
        Ok(_) => println!("Loop completed successfully (unexpected)"),
        Err(e) => println!("Loop failed as expected: {}\n", e),
    }

    println!("Final counter state: {:?}", *counter.lock().unwrap());

    println!("\n=== Summary ===");
    println!("This example demonstrates:");
    println!("1. The repeat! macro creates loop operations with rollback support");
    println!(
        "2. When FailingOp fails on iteration 2, CounterOp from that iteration is rolled back"
    );
    println!("3. Previous successful iterations (0, 1) remain committed");
    println!("4. The counter should contain values from successful iterations only");

    Ok(())
}