summaryrefslogtreecommitdiffstats
path: root/src/flat_map_x.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/flat_map_x.rs')
-rw-r--r--src/flat_map_x.rs150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/flat_map_x.rs b/src/flat_map_x.rs
new file mode 100644
index 0000000..d6cf4a8
--- /dev/null
+++ b/src/flat_map_x.rs
@@ -0,0 +1,150 @@
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+/// Extension trait for `Iterator<Item = Result<O, E>>` to selectively transform Oks and Errors.
+pub trait FlatMapX<O, E>: Sized {
+ fn flat_map_ok<U, F, O2>(self, F) -> FlatMapOk<Self, U, F>
+ where
+ F: FnMut(O) -> U,
+ U: IntoIterator<Item = O2>;
+ fn flat_map_err<U, F, E2>(self, F) -> FlatMapErr<Self, U, F>
+ where
+ F: FnMut(E) -> U,
+ U: IntoIterator<Item = E2>;
+}
+
+impl<I, O, E> FlatMapX<O, E> for I
+where
+ I: Iterator<Item = Result<O, E>> + Sized,
+{
+ fn flat_map_ok<U, F, O2>(self, f: F) -> FlatMapOk<Self, U, F>
+ where
+ F: FnMut(O) -> U,
+ U: IntoIterator<Item = O2>,
+ {
+ FlatMapOk {
+ frontiter: None,
+ iter: self,
+ f,
+ }
+ }
+ fn flat_map_err<U, F, E2>(self, f: F) -> FlatMapErr<Self, U, F>
+ where
+ F: FnMut(E) -> U,
+ U: IntoIterator<Item = E2>,
+ {
+ FlatMapErr {
+ frontiter: None,
+ iter: self,
+ f,
+ }
+ }
+}
+
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct FlatMapOk<I, U, F>
+where
+ U: IntoIterator,
+{
+ frontiter: Option<<U as IntoIterator>::IntoIter>,
+ iter: I,
+ f: F,
+}
+
+impl<I, O, E, F, O2, U> Iterator for FlatMapOk<I, U, F>
+where
+ I: Iterator<Item = Result<O, E>>,
+ F: FnMut(O) -> U,
+ U: IntoIterator<Item = O2>,
+{
+ type Item = Result<O2, E>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ if let Some(ref mut inner) = self.frontiter {
+ if let elt @ Some(_) = inner.next() {
+ return elt.map(Ok);
+ }
+ }
+ match self.iter.next() {
+ None => return None,
+ Some(Ok(x)) => {
+ self.frontiter = Some((self.f)(x).into_iter());
+ }
+ Some(Err(e)) => return Some(Err(e)),
+ }
+ }
+ }
+
+ #[inline]
+ // TODO: Oh dear, this hint could be much better
+ // https://doc.rust-lang.org/src/core/iter/mod.rs.html#2694
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct FlatMapErr<I, U: IntoIterator, F> {
+ frontiter: Option<<U as IntoIterator>::IntoIter>,
+ iter: I,
+ f: F,
+}
+
+impl<I, O, E, F, E2, U> Iterator for FlatMapErr<I, U, F>
+where
+ I: Iterator<Item = Result<O, E>>,
+ F: FnMut(E) -> U,
+ U: IntoIterator<Item = E2>,
+{
+ type Item = Result<O, E2>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ if let Some(ref mut inner) = self.frontiter {
+ if let elt @ Some(_) = inner.next() {
+ return elt.map(Err);
+ }
+ }
+ match self.iter.next() {
+ None => return None,
+ Some(Err(e)) => {
+ self.frontiter = Some((self.f)(e).into_iter());
+ }
+ Some(Ok(o)) => return Some(Ok(o)),
+ }
+ }
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+#[test]
+fn test_flat_map_ok() {
+ let mapped: Vec<_> = vec![Ok(1), Ok(2), Err(2), Err(0), Ok(2)]
+ .into_iter()
+ .flat_map_ok(|i| (0..i))
+ .flat_map_err(|i| 0..(i * 2))
+ .collect();
+
+ assert_eq!(
+ mapped,
+ [
+ Ok(0),
+ Ok(0),
+ Ok(1),
+ Err(0),
+ Err(1),
+ Err(2),
+ Err(3),
+ Ok(0),
+ Ok(1)
+ ]
+ );
+}