use core::iter::{Chain, Once, once};
#[derive(Clone, Debug)]
pub struct NonEmpty<I: Iterator> {
first: I::Item,
rest: I,
}
impl<I: Iterator> NonEmpty<I> {
pub const fn new(first: I::Item, rest: I) -> Self {
Self { first, rest }
}
pub fn try_new(mut items: I) -> Option<Self> {
let first = items.next()?;
Some(Self::new(first, items))
}
pub fn into_parts(self) -> (I::Item, I) {
(self.first, self.rest)
}
}
impl<I: Iterator> IntoIterator for NonEmpty<I> {
type Item = I::Item;
type IntoIter = Chain<Once<I::Item>, I>;
fn into_iter(self) -> Self::IntoIter {
once(self.first).chain(self.rest)
}
}
#[cfg(not(any(
commonware_stability_GAMMA,
commonware_stability_DELTA,
commonware_stability_EPSILON,
commonware_stability_RESERVED
)))] #[macro_export]
macro_rules! non_empty {
(@$items:expr) => {{
$crate::iter::NonEmpty::try_new(::core::iter::IntoIterator::into_iter($items))
.expect("iterator must be non-empty")
}};
($first:expr $(, $rest:expr)* $(,)?) => {
$crate::iter::NonEmpty::new(
$first,
::core::iter::IntoIterator::into_iter([$($rest),*]),
)
};
}
pub fn zip_eq<A: IntoIterator, B: IntoIterator>(a: A, b: B) -> ZipEq<A::IntoIter, B::IntoIter> {
ZipEq {
a: a.into_iter(),
b: b.into_iter(),
}
}
#[derive(Clone, Debug)]
pub struct ZipEq<A, B> {
a: A,
b: B,
}
impl<A: Iterator, B: Iterator> Iterator for ZipEq<A, B> {
type Item = (A::Item, B::Item);
fn next(&mut self) -> Option<Self::Item> {
match (self.a.next(), self.b.next()) {
(Some(a), Some(b)) => Some((a, b)),
(None, None) => None,
(Some(_), None) => panic!("zip_eq: right iterator exhausted first"),
(None, Some(_)) => panic!("zip_eq: left iterator exhausted first"),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (a_low, a_high) = self.a.size_hint();
let (b_low, b_high) = self.b.size_hint();
let high = match (a_high, b_high) {
(Some(a), Some(b)) => Some(a.min(b)),
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
(None, None) => None,
};
(a_low.min(b_low), high)
}
}
impl<A: ExactSizeIterator, B: ExactSizeIterator> ExactSizeIterator for ZipEq<A, B> {}
#[cfg(test)]
mod tests {
use super::{NonEmpty, zip_eq};
#[test]
fn try_new_rejects_empty() {
assert!(NonEmpty::try_new(core::iter::empty::<u8>()).is_none());
}
#[test]
fn iteration_preserves_every_item() {
let items = NonEmpty::try_new([1, 2, 3].into_iter()).expect("items are non-empty");
assert_eq!(items.into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
}
#[test]
fn into_parts_separates_first_from_rest() {
let items = NonEmpty::new(1, [2, 3].into_iter());
let (first, rest) = items.into_parts();
assert_eq!(first, 1);
assert_eq!(rest.collect::<Vec<_>>(), vec![2, 3]);
}
#[test]
fn macro_constructs_non_empty_iterators() {
assert_eq!(non_empty![1].into_iter().collect::<Vec<_>>(), vec![1]);
assert_eq!(
non_empty![1, 2, 3].into_iter().collect::<Vec<_>>(),
vec![1, 2, 3]
);
assert_eq!(
non_empty![@1..4].into_iter().collect::<Vec<_>>(),
vec![1, 2, 3]
);
}
#[test]
fn zip_eq_pairs_equal_lengths() {
let pairs: Vec<_> = zip_eq([1, 2, 3], ["a", "b", "c"]).collect();
assert_eq!(pairs, vec![(1, "a"), (2, "b"), (3, "c")]);
}
#[test]
#[should_panic(expected = "right iterator exhausted first")]
fn zip_eq_panics_when_right_is_shorter() {
zip_eq([1, 2], [1]).count();
}
#[test]
#[should_panic(expected = "left iterator exhausted first")]
fn zip_eq_panics_when_left_is_shorter() {
zip_eq([1], [1, 2]).count();
}
#[test]
#[should_panic(expected = "iterator must be non-empty")]
fn macro_rejects_empty_iterators() {
let _ = non_empty![@core::iter::empty::<u8>()];
}
mod colliding_method {
trait CollidingIntoIterator {
fn into_iter(self);
}
impl<T, const N: usize> CollidingIntoIterator for [T; N] {
fn into_iter(self) {}
}
#[test]
fn macro_ignores_colliding_into_iterator_methods() {
CollidingIntoIterator::into_iter([0]);
assert_eq!(
non_empty![1, 2, 3].into_iter().collect::<Vec<_>>(),
vec![1, 2, 3]
);
assert_eq!(
non_empty![@[1, 2, 3]].into_iter().collect::<Vec<_>>(),
vec![1, 2, 3]
);
}
}
}