| Crates.io | batch_result |
| lib.rs | batch_result |
| version | 0.1.0 |
| created_at | 2025-12-29 01:11:30.111147+00 |
| updated_at | 2025-12-29 01:11:30.111147+00 |
| description | Heuristic batch evaluation for independent tasks |
| homepage | |
| repository | https://github.com/oOp995/rust_batch_result |
| max_upload_size | |
| id | 2009725 |
| size | 14,077 |
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:
batch_result is designed for tasks where tasks are independent, and where
the goal is evaluation and reporting, not control flow or orchestration.
Test / validation pipelines
Tasks with independent phases
Partial success is meaningful
Diagnostics matter more than strict pass/fail
You want evaluation, not control flow
Strict workflows or state machines
Tasks with required execution order
Must-complete or transactional pipelines
Security-critical or atomic operations
// Traditional approach - fails at first error
let results: Result<Vec<Data>, Error> = items
.iter()
.map(process_item)
.collect()?; // Stops at first error!
//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(())
}
}
//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())
}
}
[dependencies]
batch_result = "0.1.0"
Result<Vec<T>, E>?| Approach | Behavior |
|---|---|
Result<Vec<T>, E> |
Stops at first error |
batch_result |
Collects all outcomes |