From d2f423521ec76406944ad83098ec33afe20c692b Mon Sep 17 00:00:00 2001 From: Kim Altintop Date: Mon, 9 Jan 2023 13:18:33 +0100 Subject: This is it Squashed commit of all the exploration history. Development starts here. Signed-off-by: Kim Altintop --- src/iter.rs | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/iter.rs (limited to 'src/iter.rs') diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000..1289c52 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,109 @@ +// 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: +/// +/// ```no_run +/// 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.: +/// +/// ```no_run +/// 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 {} -- cgit v1.2.3