dyn-iter 1.0.1

Wrapper around `Box<dyn Iterator<Item = V> + 'iter>` to simplify your code
Documentation
#![doc = include_str!("../README.md")]
#![warn(
    missing_docs,
    missing_copy_implementations,
    missing_debug_implementations,
    missing_docs,
    rust_2018_idioms
)]
use std::fmt::{Debug, Formatter};

/// Iterator type that can wrap any kind of [`Iterator`].
///
/// This `struct` is a wrapper around types that implements `Iterator`
/// trait. Since we do not know which specific type of `Iterator` is
/// used, we `Box` it as a trait-object.
///
/// This iterator yields any type which usually depends on references on the
/// model.  Therefore, the iterator must outlive the wrapped `Iterator`.
///
/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
pub struct DynIter<'iter, V> {
    iter: Box<dyn Iterator<Item = V> + 'iter>,
}

impl<V> Debug for DynIter<'_, V> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let size_str = match self.iter.size_hint() {
            (min, None) => format!("at least {min}",),
            (min, Some(max)) if min == max => format!("{min}",),
            (min, Some(max)) => format!("between {min} and {max}"),
        };
        write!(f, "{{ iter: [Iterator with {size_str} elements]}}")
    }
}

impl<'iter, V> DynIter<'iter, V> {
    /// Instantiates an [`DynIter`] from any kind of [`Iterator`].
    ///
    /// [`DynIter`]: ./struct.DynIter.html
    /// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
    pub fn new<I>(iter: I) -> Self
    where
        I: Iterator<Item = V> + 'iter,
    {
        Self {
            iter: Box::new(iter),
        }
    }
}

impl<'iter, V> Iterator for DynIter<'iter, V> {
    type Item = V;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }

    fn count(self) -> usize
    where
        Self: Sized,
    {
        self.iter.count()
    }
}

/// Helper for easy conversion into a dynamic iterator `DynIter`.
///
/// ```
/// # use dyn_iter::IntoDynIterator as _;
/// let dyn_iter = vec![1, 2, 3, 4, 5]
///     .into_iter()
///     .filter(|x| x % 2 == 0)
///     .map(|x| 3 * x + 1)
///     .into_dyn_iter();
/// assert_eq!(dyn_iter.count(), 2);
/// ```
pub trait IntoDynIterator: Iterator {
    /// Helper function to convert an `Iterator` into a `DynIter`.
    #[inline]
    fn into_dyn_iter<'iter>(self) -> DynIter<'iter, Self::Item>
    where
        Self: Sized + 'iter,
    {
        DynIter::new(self)
    }
}

impl<T: ?Sized> IntoDynIterator for T where T: Iterator {}

#[cfg(test)]
mod tests {
    use crate::{DynIter, IntoDynIterator as _};

    // This test is mostly checking that everything compiles and works as
    // expected.
    #[test]
    fn it_compiles() {
        let iter = (0..5).skip(2).filter(|n| *n != 3);
        let mut dyn_iter = DynIter::new(iter);
        assert_eq!(dyn_iter.size_hint(), (0, Some(3)));
        assert_eq!(dyn_iter.next(), Some(2));
        assert_eq!(dyn_iter.size_hint(), (0, Some(2)));
        assert_eq!(dyn_iter.next(), Some(4));
        assert_eq!(dyn_iter.size_hint(), (0, Some(0)));
        assert_eq!(dyn_iter.next(), None);
    }

    #[test]
    fn size_hint_count() {
        let iter = (0..5).skip(2).filter(|e| *e != 3).into_dyn_iter();
        assert_eq!(iter.size_hint(), (0, Some(3)));
        assert_eq!(iter.count(), 2);
    }

    mod debug {
        use super::*;

        struct SizeHintIterator {
            min: usize,
            max: Option<usize>,
        }

        impl Iterator for SizeHintIterator {
            type Item = u8;
            fn next(&mut self) -> Option<Self::Item> {
                unimplemented!()
            }
            fn size_hint(&self) -> (usize, Option<usize>) {
                (self.min, self.max)
            }
        }

        #[test]
        fn no_max_size_hint() {
            let iter = SizeHintIterator { min: 2, max: None };
            let dyn_iter = DynIter::new(iter);
            let debug_msg = format!("{dyn_iter:?}");
            assert_eq!("{ iter: [Iterator with at least 2 elements]}", debug_msg);
        }

        #[test]
        fn equal_min_max_size_hint() {
            let iter = SizeHintIterator {
                min: 3,
                max: Some(3),
            };
            let dyn_iter = DynIter::new(iter);
            let debug_msg = format!("{dyn_iter:?}");
            assert_eq!("{ iter: [Iterator with 3 elements]}", debug_msg);
        }

        #[test]
        fn different_min_max_size_hint() {
            let iter = SizeHintIterator {
                min: 4,
                max: Some(5),
            };
            let dyn_iter = DynIter::new(iter);
            let debug_msg = format!("{dyn_iter:?}");
            assert_eq!(
                "{ iter: [Iterator with between 4 and 5 elements]}",
                debug_msg
            );
        }
    }
}