summaryrefslogtreecommitdiffstats
path: root/lib/core/libimagerror/src/iter.rs
blob: fa6fec1a720c68980a5370d09029413fb19c6cc5 (plain)
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
128
129
130
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2020 the imag contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use failure::Error;

/// An iterator that unwraps the `Ok` items of `iter`, while passing the `Err` items to its
/// closure `f`.
///
/// This `struct` is created by the `unwrap_with()` method on `TraceIterator`. See its
/// documentation for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone)]
pub struct UnwrapWith<I, F>{
    iter: I,
    f: F
}

impl<I, F, T> Iterator for UnwrapWith<I, F>
    where
        I: Iterator<Item = Result<T, Error>>,
        F: FnMut(Error)
{
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.iter.next() {
                Some(Err(e))   => { (self.f)(e); },
                Some(Ok(item)) => return Some(item),
                None           => return None,
            }
        }
    }
}



/// Iterator helper for Unwrap with exiting on error
pub struct UnwrapExit<I, T>(I)
    where I: Iterator<Item = Result<T, Error>>;

impl<I, T> Iterator for UnwrapExit<I, T>
    where I: Iterator<Item = Result<T, Error>>,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        use crate::trace::MapErrTrace;
        self.0.next().map(|e| e.map_err_trace_exit_unwrap())
    }
}

impl<I, T> DoubleEndedIterator for UnwrapExit<I, T>
    where I: DoubleEndedIterator<Item = Result<T, Error>>,
{
    fn next_back(&mut self) -> Option<Self::Item> {
        use crate::trace::MapErrTrace;
        self.0.next_back().map(|e| e.map_err_trace_exit_unwrap())
    }
}

/// This trait provides methods that make it easier to work with iterators that yield a `Result`.
pub trait TraceIterator<T> : Iterator<Item = Result<T, Error>> + Sized {
    /// Creates an iterator that yields the item in each `Ok` item, while filtering out the
    /// `Err`
    /// items. Each filtered `Err` will be trace-logged with [`::trace::trace_error`].
    ///
    /// As with all iterators, the processing is lazy. If you do not use the result of this method,
    /// nothing will be passed to `::trace::trace_error`, no matter how many `Err` items might
    /// be present.
    #[inline]
    fn trace_unwrap(self) -> UnwrapWith<Self, fn(Error)> {
        #[inline]
        fn trace_error(err: Error) {
            err.iter_chain().for_each(|cause| {
                eprintln!("{}", cause);
            });
        }

        self.unwrap_with(trace_error)
    }

    /// Creates an iterator that yields the item in each `Ok` item.
    ///
    /// The first `Err(_)` element is traced using `::trace::trace_error_exit`.
    ///
    /// As with all iterators, the processing is lazy. If you do not use the result of this method,
    /// nothing will be passed to `::trace::trace_error_exit`, no matter how many `Err` items might
    /// be present.
    #[inline]
    fn trace_unwrap_exit(self) -> UnwrapExit<Self, T> {
        UnwrapExit(self)
    }


    /// Takes a closure and creates an iterator that will yield the items inside all `Ok` items
    /// yielded by the original iterator. All `Err` items will be filtered out, and the contents
    /// of each `Err` will be passed to the closure.
    ///
    /// As with all iterators, the processing is lazy. The result of this method must be evaluated
    /// for the closure to be called.
    #[inline]
    fn unwrap_with<F>(self, f: F) -> UnwrapWith<Self, F>
        where F: FnMut(Error)
    {
        UnwrapWith { iter: self, f }
    }
}

impl<I, T> TraceIterator<T> for I where
    I: Iterator<Item = Result<T, Error>>
{}