From 27079c067a0c3c947f5f5604d4e3df445f136c0f Mon Sep 17 00:00:00 2001 From: Kinto Date: Wed, 1 May 2024 21:26:23 +1000 Subject: [PATCH 1/3] Implement `FlattenOk::fold` --- src/flatten_ok.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/flatten_ok.rs b/src/flatten_ok.rs index 853122295..257ef70ae 100644 --- a/src/flatten_ok.rs +++ b/src/flatten_ok.rs @@ -72,6 +72,29 @@ where } } + fn fold(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + // Front + let mut acc = match self.inner_front { + Some(x) => x.fold(init, |a, o| f(a, Ok(o))), + None => init, + }; + + acc = self.iter.fold(acc, |acc, x| match x { + Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))), + Err(e) => f(acc, Err(e)), + }); + + // Back + match self.inner_back { + Some(x) => x.fold(acc, |a, o| f(a, Ok(o))), + None => acc, + } + } + fn size_hint(&self) -> (usize, Option) { let inner_hint = |inner: &Option| { inner From 10a8bb262f857eea3aea1e8be1b320d3a95237f0 Mon Sep 17 00:00:00 2001 From: Kinto Date: Wed, 1 May 2024 21:26:35 +1000 Subject: [PATCH 2/3] Implement `FlattenOk::rfold` --- src/flatten_ok.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/flatten_ok.rs b/src/flatten_ok.rs index 257ef70ae..48f1e90a6 100644 --- a/src/flatten_ok.rs +++ b/src/flatten_ok.rs @@ -153,6 +153,29 @@ where } } } + + fn rfold(self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + // Back + let mut acc = match self.inner_back { + Some(x) => x.rfold(init, |a, o| f(a, Ok(o))), + None => init, + }; + + acc = self.iter.rfold(acc, |acc, x| match x { + Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))), + Err(e) => f(acc, Err(e)), + }); + + // Front + match self.inner_front { + Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))), + None => acc, + } + } } impl Clone for FlattenOk From 568021df875b979be4089640c9e9d150057e8572 Mon Sep 17 00:00:00 2001 From: Kinto Date: Sun, 5 May 2024 00:22:47 +1000 Subject: [PATCH 3/3] Implement two-element 'vector' for `flatten_ok` test Previously the test used Option but the coverage was bad. We cannot use Vec because it is too slow. --- tests/specializations.rs | 64 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/tests/specializations.rs b/tests/specializations.rs index cb2141053..712311472 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -1,7 +1,9 @@ #![allow(unstable_name_collisions)] use itertools::Itertools; +use quickcheck::Arbitrary; use quickcheck::{quickcheck, TestResult}; +use rand::Rng; use std::fmt::Debug; struct Unspecialized(I); @@ -452,8 +454,8 @@ quickcheck! { test_specializations(&v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None })); } - // `Option` because `Vec` would be very slow!! And we can't give `[u8; 3]`. - fn flatten_ok(v: Vec, char>>) -> () { + // `SmallIter2` because `Vec` is too slow and we get bad coverage from a singleton like Option + fn flatten_ok(v: Vec, char>>) -> () { let it = v.into_iter().flatten_ok(); test_specializations(&it); test_double_ended_specializations(&it); @@ -520,3 +522,61 @@ quickcheck! { } } } + +/// Like `VecIntoIter` with maximum 2 elements. +#[derive(Debug, Clone, Default)] +enum SmallIter2 { + #[default] + Zero, + One(T), + Two(T, T), +} + +impl Arbitrary for SmallIter2 { + fn arbitrary(g: &mut G) -> Self { + match g.gen_range(0u8, 3) { + 0 => Self::Zero, + 1 => Self::One(T::arbitrary(g)), + 2 => Self::Two(T::arbitrary(g), T::arbitrary(g)), + _ => unreachable!(), + } + } + // maybe implement shrink too, maybe not +} + +impl Iterator for SmallIter2 { + type Item = T; + + fn next(&mut self) -> Option { + match std::mem::take(self) { + Self::Zero => None, + Self::One(val) => Some(val), + Self::Two(val, second) => { + *self = Self::One(second); + Some(val) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = match self { + Self::Zero => 0, + Self::One(_) => 1, + Self::Two(_, _) => 2, + }; + (len, Some(len)) + } +} + +impl DoubleEndedIterator for SmallIter2 { + fn next_back(&mut self) -> Option { + match std::mem::take(self) { + Self::Zero => None, + Self::One(val) => Some(val), + Self::Two(first, val) => { + *self = Self::One(first); + Some(val) + } + } + } +}