batch_result

Crates.iobatch_result
lib.rsbatch_result
version0.1.0
created_at2025-12-29 01:11:30.111147+00
updated_at2025-12-29 01:11:30.111147+00
descriptionHeuristic batch evaluation for independent tasks
homepage
repositoryhttps://github.com/oOp995/rust_batch_result
max_upload_size
id2009725
size14,077
oOp (oOp995)

documentation

README

Crates.io Docs.rs

batch_result

Do not fail fast ,collect results and errors then decide,

batch_result is dynamic result batch handling, aggregates results from multiple independent tasks and classifies the overall outcome heuristically (e.g. Excellent, Partial, Poor).

This crate intentionally does not model workflows:

  • it does not enforce execution order
  • it does not express dependencies between phases
  • it does not require mandatory completion of any phase

batch_result is designed for tasks where tasks are independent, and where the goal is evaluation and reporting, not control flow or orchestration.

When to Use batch_result

  • Test / validation pipelines

  • Tasks with independent phases

  • Partial success is meaningful

  • Diagnostics matter more than strict pass/fail

  • You want evaluation, not control flow

When Not to Use batch_result

  • Strict workflows or state machines

  • Tasks with required execution order

  • Must-complete or transactional pipelines

  • Security-critical or atomic operations

The Problem

// Traditional approach - fails at first error
let results: Result<Vec<Data>, Error> = items
    .iter()
    .map(process_item)
    .collect()?; // Stops at first error!

The Solution

//add batch_result and toml to your dependencies 
//for this example

//BoxedDynError is defined inside batch_result module
//as Box<dyn Error + Send +Sync>

fn main()->Result<(),batch_result::BoxedDynError>{
    // main syntax
    //add mut keyword in order to add tasks
    let mut outcome=batch_result::Outcome::new();
    outcome
        .task("config",health_check::check_config)
        .task("database", health_check::check_database)
        .task("cache", health_check::check_cache)
        .run();//. if  you didnt call .run(), the .classify() will produce OutcomeClass::Empty
    //in other words,⚠️ Tasks are **lazy**
    //Nothing executes until you call `.run()`

    match outcome.classify() {
        //review enum batch_result::OutcomeClass for better understanding
        batch_result::OutcomeClass::Excellent =>{
            println!("Service healthy, starting...");
            Ok(())
        },
        _=>{
            eprintln!("startup checks failed:");
            for (taskname,err) in outcome.map_errors()  {
                eprintln!("task {taskname} failed - {err}")
            }
            Err("startup aborted ! ".into())
        }
    }
}

//helpers to simulate the tasks 
mod health_check{
    use std::{error::Error, fs};
    type BoxedDynError=Box<dyn Error + Send + Sync>;
    pub fn check_config()->Result< () ,BoxedDynError>{
        let content=fs::read_to_string("config.toml")?;
        toml::from_str::<toml::Value>(&content)?;
        Ok(())
    }

    pub fn check_database()->Result< () ,BoxedDynError>{
        //simulate db ping
        if std::env::var("DB_URL").is_err(){
            return Err("DB_URL not set".into());
        }
        Ok(())
    }

    pub fn check_cache()->Result< () ,BoxedDynError>{
        fs::create_dir_all("/tmp/myapp-cache")?;
        Ok(())
    }
}

Another Example with Concrete return types

//add batch_result to your dependencies
fn main(){
    //concrete success type
    //note that:
    // Outcome is generic over T
    // all tasks must return Result<T, BoxedDynError>
    //Errors may differ and are erased into `BoxedDynError`.
    let mut outcome=batch_result::Outcome::new();
    outcome
        .task("cpu_temp", health_check::read_cpu_temp)
        .task("disk_free", health_check::read_disk_free)
        .task("mem_free", health_check::read_mem_free)
        .run();

    println!("System health : {:?}",outcome.classify());

    for (name,value) in outcome.map_success(){
        println!("{name} : {value}");
    }
    
}

//helpers to demonstrate only
mod health_check{
    use std::error::Error;
    use batch_result::BoxedDynError;
    pub fn read_cpu_temp()->Result<f64,BoxedDynError>{
        //Simulated sensor read
        Ok(63.4)
    }

    pub fn read_disk_free()->Result<f64,BoxedDynError>{
        //Simulated disk query
        Ok(128.2)
    }

    pub fn read_mem_free()->Result<f64,BoxedDynError>{
        Err("memory sensor unavailable".into())
    }
}

Installation

[dependencies]
batch_result = "0.1.0"

Why not Result<Vec<T>, E>?

Approach Behavior
Result<Vec<T>, E> Stops at first error
batch_result Collects all outcomes
Commit count: 0

cargo fmt