ops-rs 1.64.597

A Rust ops framework with composable wrappers and batch execution
Documentation

Ops - Rust Ops Framework

Rust ops framework with dry/wet context separation and deferred execution.

Features

  • Dry/Wet Context separation - serializable data vs runtime references
  • Op metadata and schema validation for inputs, references, and outputs
  • Deferred execution - save op requests for later execution
  • Composable wrappers - logging and timeout decorators
  • Batch operations - sequential and parallel execution
  • Ergonomic macros - context access patterns
  • Memory safety with zero-cost abstractions

Quick Start

Add to your Cargo.toml:

[dependencies]
ops = { path = "." }
tokio = { version = "1.0", features = ["full"] }

Basic Usage

use ops_rs::{Op, DryContext, WetContext, OpMetadata, perform};
use async_trait::async_trait;

struct GreetingOp;

#[async_trait]
impl Op<String> for GreetingOp {
    async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> Result<String, ops_rs::OpError> {
        let name = dry.get_required::<String>("name")?;
        Ok(format!("Hello, {}!", name))
    }
    
    fn metadata(&self) -> OpMetadata {
        OpMetadata::builder("GreetingOp")
            .description("Greets a person by name")
            .build()
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut dry = DryContext::new().with_value("name", "World");
    let mut wet = WetContext::new();
    
    let op = Box::new(GreetingOp);
    let result = perform(op, &mut dry, &mut wet).await?;
    println!("{}", result);
    
    Ok(())
}

Wrapper Composition

use ops_rs::{LoggingWrapper, TimeBoundWrapper, DryContext, WetContext};
use std::time::Duration;

let op = Box::new(GreetingOp);
let timeout_op = TimeBoundWrapper::new(op, Duration::from_secs(5));
let logged_op = LoggingWrapper::new(Box::new(timeout_op), "GreetingOp".to_string());

let dry = DryContext::new().with_value("name", "World");
let wet = WetContext::new();

let result = logged_op.perform(&dry, &wet).await?;

Batch Operations

use ops_rs::{BatchOp, DryContext, WetContext};
use std::sync::Arc;

let ops: Vec<Arc<dyn Op<String>>> = vec![
    Arc::new(GreetingOp),
    Arc::new(GreetingOp),
];

let mut dry = DryContext::new().with_value("name", "Alice");
let mut wet = WetContext::new();

let batch = BatchOp::new(ops);
let results = batch.perform(&mut dry, &mut wet).await?;

Architecture

Core Components

  • Op Trait: Async trait with metadata and validation
  • Dry/Wet Contexts: Separation of data from references
  • Wrapper Pattern: Composable decorators
  • Batch Operations: Sequential and parallel execution
  • Deferred Execution: Save and execute later

Context System

  • DryContext: Serializable data (can be persisted)
  • WetContext: Runtime references (services, connections)
  • Schema Validation: JSON Schema validation
  • Deferred Execution: Save op requests for later

Macros

Ergonomic context access patterns:

use ops_rs::{dry_put, dry_require, wet_put_ref, wet_require_ref};

// Dry context (serializable data)
dry_put!(dry, user_id);
let name: String = dry_require!(dry, name)?;

// Wet context (runtime references)
wet_put_ref!(wet, database);
let db: Arc<Database> = wet_require_ref!(wet, database)?;

Available macros:

  • dry_put!, dry_get!, dry_require!, dry_result!
  • wet_put_ref!, wet_put_arc!, wet_get_ref!, wet_require_ref!

Examples

Run examples to see the framework in action:

cargo run --example dry_wet_context_demo
cargo run --example macro_usage_demo

Schema Validation

Define input, reference, and output schemas:

fn metadata(&self) -> OpMetadata {
    OpMetadata::builder("UserOp")
        .input_schema(json!({
            "type": "object",
            "properties": {
                "user_id": {"type": "string"}
            },
            "required": ["user_id"]
        }))
        .reference_schema(json!({
            "type": "object",
            "properties": {
                "database": {"type": "DatabaseService"}
            },
            "required": ["database"]
        }))
        .output_schema(json!({
            "type": "object",
            "properties": {
                "status": {"type": "string"}
            }
        }))
        .build()
}

// Validate contexts before execution
let validation = op.metadata().validate_contexts(&dry, &wet)?;
if validation.is_valid {
    let result = op.perform(&dry, &wet).await?;
}

Testing

cargo test                          # Unit tests
cargo test --test integration_tests # Integration tests

License

MIT License - see LICENSE file for details.