// // imag - the personal information management suite for the commandline // Copyright (C) 2015-2018 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 error_chain::ChainedError; /// 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{ iter: I, f: F } impl Iterator for UnwrapWith where I: Iterator>, F: FnMut(E) { type Item = T; #[inline] fn next(&mut self) -> Option { loop { match self.iter.next() { Some(Err(e)) => { (self.f)(e); }, Some(Ok(item)) => return Some(item), None => return None, } } } #[inline] fn size_hint(&self) -> (usize, Option) { let (_, upper) = self.iter.size_hint(); (0, upper) } } impl DoubleEndedIterator for UnwrapWith where I: DoubleEndedIterator>, F: FnMut(E) { #[inline] fn next_back(&mut self) -> Option { loop { match self.iter.next_back() { 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, i32) where I: Iterator>, E: ChainedError; impl Iterator for UnwrapExit where I: Iterator>, E: ChainedError { type Item = T; fn next(&mut self) -> Option { use trace::MapErrTrace; self.0.next().map(|e| e.map_err_trace_exit_unwrap(self.1)) } } impl DoubleEndedIterator for UnwrapExit where I: DoubleEndedIterator>, E: ChainedError { fn next_back(&mut self) -> Option { use trace::MapErrTrace; self.0.next_back().map(|e| e.map_err_trace_exit_unwrap(self.1)) } } /// This trait provides methods that make it easier to work with iterators that yield a `Result`. pub trait TraceIterator : Iterator> + 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 where E: ChainedError { #[inline] fn trace_error>(err: E) { eprintln!("{}", err.display_chain()); } 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, exitcode: i32) -> UnwrapExit where E: ChainedError { UnwrapExit(self, exitcode) } /// 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(self, f: F) -> UnwrapWith where F: FnMut(E) { UnwrapWith { iter: self, f: f } } } impl TraceIterator for I where I: Iterator> {} #[cfg(test)] mod test { use super::TraceIterator; #[derive(Copy, Clone, Eq, PartialEq, Debug)] struct TestError(i32); #[test] fn test_unwrap_with() { let original = vec![Ok(1), Err(TestError(2)), Ok(3), Err(TestError(4))]; let mut errs = vec![]; let oks = original .into_iter() .unwrap_with(|e|errs.push(e)) .collect::>(); assert_eq!(&oks, &[1, 3]); assert_eq!(&errs, &[TestError(2), TestError(4)]); } #[test] fn test_unwrap_with_backward() { let original = vec![Ok(1), Err(TestError(2)), Ok(3), Err(TestError(4))]; let mut errs = vec![]; let oks = original .into_iter() .rev() .unwrap_with(|e|errs.push(e)) .collect::>(); assert_eq!(&oks, &[3, 1]); assert_eq!(&errs, &[TestError(4), TestError(2)]); } }