// Copyright © 2022 Kim Altintop // SPDX-License-Identifier: GPL-2.0-only WITH openvpn-openssl-exception use std::ops::ControlFlow; /// Iterator with a lazy fallible initialiser /// /// It is a common pattern that instantiating an effectful iterator is fallible, /// while traversing it is fallible, too. This yields unwieldy signatures like: /// /// ```ignore /// fn my_iterator() -> Result>, E> /// ``` /// /// Often, however, we can unify the error types (`E` and `F` above), which /// allows for the more pleasant pattern that constructing the iterator is /// infallible, but an initialiser error is returned upon the first call to /// `next()`. Ie.: /// /// ```ignore /// fn my_iterator() -> impl Iterator> /// ``` #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct Iter { init: Option, iter: Option>, next: G, } impl Iter { pub fn new(init: F, next: G) -> Self { Self { init: Some(init), iter: None, next, } } } impl Iterator for Iter where F: FnOnce() -> Result, I: Iterator>, G: FnMut(Result) -> Option>, { type Item = Result; fn next(&mut self) -> Option { match self.iter.take() { None => { let init = self.init.take()?; self.iter = Some(init()); self.next() }, Some(Err(e)) => Some(Err(e)), Some(Ok(mut iter)) => { let item = iter.next()?; let next = (self.next)(item); self.iter = Some(Ok(iter)); next }, } } } impl DoubleEndedIterator for Iter where F: FnOnce() -> Result, I: Iterator> + DoubleEndedIterator, G: FnMut(Result) -> Option>, { fn next_back(&mut self) -> Option { match self.iter.take() { None => { let init = self.init.take()?; self.iter = Some(init()); self.next_back() }, Some(Err(e)) => Some(Err(e)), Some(Ok(mut iter)) => { let item = iter.next_back()?; let next = (self.next)(item); self.iter = Some(Ok(iter)); next }, } } } pub(crate) trait IteratorExt { fn try_find_map(&mut self, mut f: F) -> crate::Result> where Self: Iterator + Sized, F: FnMut(Self::Item) -> Result, E>, E: Into, { let x = self.try_fold((), |(), i| match f(i) { Err(e) => ControlFlow::Break(Err(e.into())), Ok(v) if v.is_some() => ControlFlow::Break(Ok(v)), Ok(_) => ControlFlow::Continue(()), }); match x { ControlFlow::Continue(()) => Ok(None), ControlFlow::Break(v) => v, } } } impl IteratorExt for T {}