gazebo/src/ext/iter.rs (191 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under both the MIT license found in the * LICENSE-MIT file in the root directory of this source tree and the Apache * License, Version 2.0 found in the LICENSE-APACHE file in the root directory * of this source tree. */ use std::{cmp::Ordering, iter::Cloned}; use crate::dupe::Dupe; /// Extension traits on [`Iterator`](Iterator). pub trait IterExt { type Item; /// Like `any`, except allow the function supplied to return a `Result` type, where we `Err` /// on the first encounter of `Err`. /// /// ``` /// use gazebo::prelude::*; /// /// fn true_if_even_throw_on_zero(x: &usize) -> Result<bool, ()> { /// if *x == 0 { /// Err(()) /// } else { /// Ok(x % 2 == 0) /// } /// } /// /// let x = [1, 3, 2]; /// assert_eq!(x.iter().try_any(true_if_even_throw_on_zero), Ok(true)); /// /// let x = [1, 3, 5]; /// assert_eq!(x.iter().try_any(true_if_even_throw_on_zero), Ok(false)); /// /// let x = [1, 0, 2]; /// assert_eq!(x.iter().try_any(true_if_even_throw_on_zero), Err(())); /// /// ``` fn try_any<F, E>(self, any: F) -> Result<bool, E> where Self: Sized, F: FnMut(Self::Item) -> Result<bool, E>; /// Like `all`, except allow the function supplied to return a `Result` type, where we `Err` /// on the first encounter of `Err`. /// /// ``` /// use gazebo::prelude::*; /// /// fn true_if_even_throw_on_zero(x: &usize) -> Result<bool, ()> { /// if *x == 0 { /// Err(()) /// } else { /// Ok(x % 2 == 0) /// } /// } /// /// let x = [2, 4, 2]; /// assert_eq!(x.iter().try_all(true_if_even_throw_on_zero), Ok(true)); /// /// let x = [1, 3, 5]; /// assert_eq!(x.iter().try_all(true_if_even_throw_on_zero), Ok(false)); /// /// let x = [2, 0, 2]; /// assert_eq!(x.iter().try_all(true_if_even_throw_on_zero), Err(())); /// /// ``` fn try_all<F, E>(self, any: F) -> Result<bool, E> where Self: Sized, F: FnMut(Self::Item) -> Result<bool, E>; /// Like `eq_by`, except allow the function supplied to return a `Result` type, where we `Err` /// on the first encounter of `Err`. /// /// ``` /// use gazebo::prelude::*; /// /// fn double_eq_throw_on_zero(x: &usize, y: &usize) -> Result<bool, ()> { /// if *x == 0 || *y == 0 { /// Err(()) /// } else { /// Ok(x * 2 == *y) /// } /// } /// /// let x = [1, 4, 2]; /// let y = [2, 8, 4]; /// /// assert_eq!(x.iter().try_eq_by(&y, double_eq_throw_on_zero), Ok(true)); /// /// let x = [1, 4, 2]; /// let y = [2, 0, 4]; /// /// assert_eq!(x.iter().try_eq_by(&y, double_eq_throw_on_zero), Err(())); /// ``` fn try_eq_by<I, F, E>(self, other: I, eq: F) -> Result<bool, E> where Self: Sized, I: IntoIterator, F: FnMut(Self::Item, I::Item) -> Result<bool, E>; /// Like `cmp_by`, except allow the function supplied to return a `Result` type, where we `Err` /// on the first encounter of `Err`. /// /// ``` /// use gazebo::prelude::*; /// use std::cmp::Ordering; /// /// fn double_cmp_throw_on_zero(x: &usize, y: &usize) -> Result<Ordering, ()> { /// if *x == 0 || *y == 0 { /// Err(()) /// } else { /// Ok((x * 2).cmp(y)) /// } /// } /// /// let x = [1, 4, 2]; /// let y = [2, 8, 4]; /// /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Equal)); /// /// let x = [1, 2, 2]; /// let y = [2, 8, 4]; /// /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Less)); /// /// let x = [1, 4]; /// let y = [2, 8, 4]; /// /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Less)); /// /// let x = [1, 4, 4]; /// let y = [2, 8, 4]; /// /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Greater)); /// /// let x = [1, 4, 2, 3]; /// let y = [2, 8, 4]; /// /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Ok(Ordering::Greater)); /// /// let x = [1, 4, 2]; /// let y = [2, 0, 4]; /// /// assert_eq!(x.iter().try_cmp_by(&y, double_cmp_throw_on_zero), Err(())); /// ``` fn try_cmp_by<I, F, E>(self, other: I, cmp: F) -> Result<Ordering, E> where Self: Sized, I: IntoIterator, F: FnMut(Self::Item, I::Item) -> Result<Ordering, E>; /// Like `unzip`, except allowing the current `Iterator` to contain `Result` type, where we `Err` /// on the first encounter of `Err`. /// /// ``` /// use gazebo::prelude::*; /// /// let i = vec![Ok::<_, ()>((1, "a")), Ok((2, "b"))]; /// /// assert_eq!(i.into_iter().try_unzip(), Ok((vec![1, 2], vec!["a", "b"]))); /// /// let i = vec![Ok((1, "a")), Err(()), Ok((2, "b"))]; /// /// assert_eq!(i.into_iter().try_unzip::<_, _, Vec<_>, Vec<_>, _>(), Err(())); /// ``` fn try_unzip<A, B, FromA, FromB, E>(self) -> Result<(FromA, FromB), E> where FromA: Default + Extend<A>, FromB: Default + Extend<B>, Self: Iterator<Item = Result<(A, B), E>>; /// If this iterator contains a single element, return it. Otherwise, return `None`. /// /// ``` /// use gazebo::prelude::*; /// /// let i = vec![1]; /// assert_eq!(i.into_iter().into_singleton(), Some(1)); /// /// let i = Vec::<i64>::new(); /// assert_eq!(i.into_iter().into_singleton(), None); /// /// let i = vec![1, 2]; /// assert_eq!(i.into_iter().into_singleton(), None); /// ``` fn into_singleton(self) -> Option<Self::Item> where Self: Sized; } pub trait IterDuped: Sized { /// Like `duped()`, but only works for types that implement `Dupe`. /// Note that the return type is deliberately `Cloned`, as that behaves /// the same as a `Duped` would be, but can take advantage of standard library /// optimisations. /// /// ``` /// use gazebo::prelude::*; /// use std::rc::Rc; /// let inputs = vec![Rc::new("Hello"), Rc::new("World")]; /// let outputs = inputs.iter().duped().collect::<Vec<_>>(); /// assert_eq!(inputs, outputs); /// ``` /// use gazebo::prelude::*; /// use std::cmp::Ordering; fn duped(self) -> Cloned<Self>; } pub trait IterOwned: Sized { /// Calls `to_owned()` on all the items provided by the inner Iterator. /// /// ``` /// use gazebo::prelude::*; /// /// let inputs = vec!["a", "b", "c"]; /// let outputs = inputs.into_iter().owned().collect::<Vec<_>>(); /// assert_eq!(outputs, vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]) /// ``` fn owned(self) -> Owned<Self>; } impl<I> IterExt for I where I: Iterator, { type Item = I::Item; fn try_any<F, E>(mut self, f: F) -> Result<bool, E> where Self: Sized, F: FnMut(Self::Item) -> Result<bool, E>, { // TODO migrate use of Result<(), Option<E>> to ControlFlow when it's no longer unstable fn check<T, E>( mut f: impl FnMut(T) -> Result<bool, E>, ) -> impl FnMut((), T) -> Result<(), Option<E>> { move |(), x| match f(x) { Ok(true) => Err(None), Ok(false) => Ok(()), Err(e) => Err(Some(e)), } } match self.try_fold((), check(f)) { Ok(()) => Ok(false), Err(None) => Ok(true), Err(Some(e)) => Err(e), } } fn try_all<F, E>(mut self, f: F) -> Result<bool, E> where Self: Sized, F: FnMut(Self::Item) -> Result<bool, E>, { // TODO migrate use of Result<(), Option<E>> to ControlFlow when it's no longer unstable fn check<T, E>( mut f: impl FnMut(T) -> Result<bool, E>, ) -> impl FnMut((), T) -> Result<(), Option<E>> { move |(), x| match f(x) { Ok(true) => Ok(()), Ok(false) => Err(None), Err(e) => Err(Some(e)), } } match self.try_fold((), check(f)) { Ok(()) => Ok(true), Err(None) => Ok(false), Err(Some(e)) => Err(e), } } fn try_eq_by<O, F, E>(mut self, other: O, mut eq: F) -> Result<bool, E> where Self: Sized, O: IntoIterator, F: FnMut(Self::Item, O::Item) -> Result<bool, E>, { let mut other = other.into_iter(); loop { let x = match self.next() { None => return Ok(other.next().is_none()), Some(val) => val, }; let y = match other.next() { None => return Ok(false), Some(val) => val, }; if !eq(x, y)? { return Ok(false); } } } fn try_cmp_by<O, F, E>(mut self, other: O, mut cmp: F) -> Result<Ordering, E> where Self: Sized, O: IntoIterator, F: FnMut(Self::Item, O::Item) -> Result<Ordering, E>, { let mut other = other.into_iter(); loop { let x = match self.next() { None => { if other.next().is_none() { return Ok(Ordering::Equal); } else { return Ok(Ordering::Less); } } Some(val) => val, }; let y = match other.next() { None => return Ok(Ordering::Greater), Some(val) => val, }; match cmp(x, y)? { Ordering::Equal => {} non_eq => return Ok(non_eq), } } } fn try_unzip<A, B, FromA, FromB, E>(self) -> Result<(FromA, FromB), E> where FromA: Default + Extend<A>, FromB: Default + Extend<B>, Self: Iterator<Item = Result<(A, B), E>>, { let mut ts: FromA = Default::default(); let mut us: FromB = Default::default(); for e in self { let (t, u) = e?; ts.extend(Some(t)); us.extend(Some(u)); } Ok((ts, us)) } fn into_singleton(mut self) -> Option<Self::Item> where Self: Sized, { let ret = self.next()?; if self.next().is_some() { return None; } Some(ret) } } impl<'a, I, T> IterDuped for I where I: Sized, I: Iterator<Item = &'a T>, T: 'a + Dupe, { fn duped(self) -> Cloned<Self> { self.cloned() } } impl<'a, I, T> IterOwned for I where I: Iterator<Item = &'a T> + Sized, T: 'a + ToOwned + ?Sized, { fn owned(self) -> Owned<Self> { Owned { inner: self } } } /// An Iterator that yields the Owned variants of the inner iterator's items. pub struct Owned<I> { inner: I, } impl<'a, I, T> Iterator for Owned<I> where I: Iterator<Item = &'a T>, T: 'a + ToOwned + ?Sized, { type Item = <T as ToOwned>::Owned; fn next(&mut self) -> Option<Self::Item> { Some(self.inner.next()?.to_owned()) } }