Crate process_results[][src]

Expand description

This crate facilitates processing an iterator of some Result type. It provides the same functionality provided by Itertools::process_results, hence the name, but with a more much ergonomic interface, some extra helper methods and a macro to reduce boiler-plate.

At a high level this crate is composed of 3 items: an extension trait IterResult that is implemented for all iterators of Result type, Fallible struct that wraps the iterator, and ErrorCollector that stores the errors.

IterResult is an extension trait that contains methods that consumes itself and wrap it with Fallible and appropriate the error collector.

Fallible has methods Fallible::process and Fallible::process_no_discard that accept a closure, which allows the caller to process an impl Iterator<Item = Result<T, E>> as an impl Iterator<Item = T> and to handle the errors in a composable manner.

ErrorCollector is a trait that let the implementor determine how errors are stored, whether or not an error shall stop the iteration, as well as how should errors be returned.
Implementations are provided for common types like Option and Vec to allow the iteration to stop and return the first error encountered and return, or to finish the iteration and stop all errors in a Vec. Unit struct Ignore is also provided that ignores all the errors encountered.

Examples

Simple Iteration

use process_results::IterResult;

let v = vec![Ok(1i64), Ok(4), Ok(-3), Err("Error"), Ok(10)];
let res: Result<i64, _> = v.into_iter().failfast().process(|it| it.sum());
assert_eq!(res, Err("Error"));

Accumulate Errors

use process_results::IterResult;

let v = vec![
    Ok(1i64),
    Err("Error1"),
    Ok(4),
    Ok(-3),
    Err("Error2"),
    Ok(10),
];
let res: Result<i64, _> = v
    .into_iter()
    .accumulate()
    .process(|it| it.sum());
assert_eq!(res, Err(vec!["Error1", "Error2"]));

Nested Errors

Here is an example that read lines from files in a folder, parse each line as i32 while saving the lines that cannot be parsed successfully.

use process_results::*;
use process_results::fallible;
use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;

let res_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("res");
let res_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("res");
let (sum, err) = res_dir
    .read_dir()
    .unwrap()
    .failfast()
    .process(|it| {
        it.filter(|entry| entry.file_type().map_or(false, |t| t.is_file()))
            .map(|entry| {
                File::open(entry.path())
                    .map(BufReader::new)
                    .map(|f| (entry.file_name(), f))
            })
            .failfast()
            .process(|it| {
                it.flat_map(|(name, f)| {
                    f.lines()
                        .enumerate()
                        .map(move |(ln_no, ln)| ln.map(|ln| (name.clone(), ln_no, ln)))
                })
                .failfast()
                .process(|it| {
                    it.map(|(name, ln_no, ln)| {
                        ln.parse::<i32>().map_err(|_e| {
                            format!("{}-{}: {}", name.to_string_lossy(), ln_no + 1, ln)
                        })
                    })
                    .accumulate()
                    .process_no_discard::<_, i32>(|it| it.sum())
                })
            })
    })
    .unwrap()
    .unwrap()
    .unwrap();
assert_eq!(sum, 11966);
assert_eq!(
    err.unwrap(),
    vec![
        "test1.txt-7: sadfs",
        "test2.txt-3: 1000000000000000000000000000000000000000000000000000000000",
        "test2.txt-6: hello world",
        "test2.txt-8: 1.35"
    ]
);

Nested Errors with Macro

The same code as the last one, but utilizing macro fallible!.

use process_results::*;
use process_results::fallible;
use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;

let res_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("res");
let (sum, err) = fallible!(
    res_dir.read_dir().unwrap().failfast(),
    |it| it
        .filter(|entry| entry.file_type().map_or(false, |t| t.is_file()))
        .map(|entry| File::open(entry.path()).map(BufReader::new)
        .map(|f| (entry.file_name(), f))).failfast(),
    |it| it.flat_map(
        |(name, f)| f.lines()
            .enumerate()
            .map(move |(ln_no, ln)| ln.map(|ln| (name.clone(), ln_no, ln)))
    ).failfast(),
    |it| it
        .map(
            |(name, ln_no, ln)| ln.parse::<i32>()
                .map_err(|_e| format!("{}-{}: {}", name.to_string_lossy(), ln_no + 1, ln))
        )
        .accumulate(),
    no_discard i32: |it| it.sum()
).unwrap().unwrap().unwrap();
assert_eq!(sum, 11966);
assert_eq!(
    err.unwrap(),
    vec![
        "test1.txt-7: sadfs",
        "test2.txt-3: 1000000000000000000000000000000000000000000000000000000000",
        "test2.txt-6: hello world",
        "test2.txt-8: 1.35"
    ]
);

Modules

Macros

A macro used to reduce boilerplate when nesting multiple calls to process or process_no_discard inside each other.

Structs

Traits

An extension trait implemented for all iterators of Result types.