1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::result::Result;
use errors::{Error, ErrorKind, ResultExt};

pub trait CumulativeErrorCollector<R> {
    fn collect_if_no_errors(self) -> Result<Vec<R>, Error>;
}

impl<T, R, E> CumulativeErrorCollector<R> for T
    where T: Iterator<Item=Result<R, E>>,
          E: Into<Error>
{
    fn collect_if_no_errors(self) -> Result<Vec<R>, Error> {
        let mut results = Vec::new();
        let mut errors = Vec::new();

        for item in self {
            match item {
                Ok(item) => results.push(item),
                Err(err) => errors.push(err.into())
            }
        }
        if errors.is_empty() {
            return Ok(results)
        }

        Err(ErrorKind::CumulativeError(errors).into())
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::fmt::Debug;

    #[test]
    fn returns_ok_when_all_results_were_ok() {
        let results: Vec<Result<(), Error>> = vec![Ok(()), Ok(()), Ok(())];

        let actual = results.into_iter().collect_if_no_errors();

        assert!(actual.is_ok())
    }

    #[test]
    fn returns_error_if_there_is_one_error() {
        let results: Vec<Result<(), Error>> = vec![Ok(()), Err("dummy".into()), Ok(())];

        let actual = results.into_iter().collect_if_no_errors();

        assert!(actual.is_err());
    }

    #[test]
    fn returns_cumulative_error_if_there_is_one_error() {
        let results: Vec<Result<(), Error>> = vec![Ok(()), Err("dummy".into()), Ok(())];

        let actual = results.into_iter().collect_if_no_errors();

        assert_error(
            "1 error(s) occured:\n".to_owned() +
            "Error no. 1: dummy\n",
            actual
        );
    }

    #[test]
    fn returns_cumulative_errors_if_there_are_three_error() {
        let results: Vec<Result<(), Error>> = vec![Err("first".into()), Err("other".into()), Err("last".into())];

        let actual = results.into_iter().collect_if_no_errors();

        assert_error(
            "3 error(s) occured:\n".to_owned() +
            "Error no. 1: first\n" +
            "Error no. 2: other\n" +
            "Error no. 3: last\n",
            actual
        );
    }

    #[test]
    fn returns_cumulative_error_with_cause() {;
        let results = vec![
            (Err("root cause".into()) as Result<(), Error>)
            .chain_err(|| "intermediate cause")
            .chain_err(|| "error")
        ];

        let actual = results.into_iter().collect_if_no_errors();

        assert_error(
            "1 error(s) occured:\n".to_owned() +
            "Error no. 1: error\n" +
            "  caused by: intermediate cause\n" +
            "  caused by: root cause\n",
            actual
        );
    }

    #[test]
    fn returns_cause_for_every_inner_error() {;
        let results = vec![
            (Err("first cause".into()) as Result<(), Error>)
            .chain_err(|| "first error"),
            (Err("second cause".into()) as Result<(), Error>)
            .chain_err(|| "second error"),
        ];

        let actual = results.into_iter().collect_if_no_errors();

        assert_error(
            "2 error(s) occured:\n".to_owned() +
            "Error no. 1: first error\n" +
            "  caused by: first cause\n" +
            "Error no. 2: second error\n" +
            "  caused by: second cause\n",
            actual
        );
    }

    fn assert_error<T: Debug>(expected: String, result: Result<T, Error>) {
        let actual = format!("{}", result.unwrap_err());

        assert_eq!(expected, actual)
    }
}