ranges 0.3.3

This crate provides a generic alternative to core/std ranges, set-operations to work with them and a range set that can efficiently store them with the least amount of memory possible.
Documentation
use core::ops::Bound;

use crate::domain::Iterable;
use crate::GenericRange;

/// A stateful `Iterator` over a `GenericRange<T>`, yielding `T`.
#[derive(Debug)]
pub struct GenericIterator<T: Iterable> {
    /// The current state of the `Iterator`.
    current: Option<T>,
    /// The end of the `GenericRange<T>` of values to be iterated over.
    end: Bound<T>,
}

impl<T> IntoIterator for GenericRange<T>
where
    T: Iterable<Output = T>,
{
    type Item = T;
    type IntoIter = GenericIterator<T>;

    /// # Panics
    /// This function panics when trying to convert a range with an unbound start, as there is no
    /// way to determine where to start iterating at. However, if the domain of `T` has a minimum,
    /// calling this is safe, as it was set as the start when the range was constructed.
    #[allow(clippy::panic)]
    fn into_iter(self) -> Self::IntoIter {
        GenericIterator::<T> {
            current: match self.start {
                Bound::Included(current) => Some(current),
                Bound::Excluded(current) => current.next(),
                Bound::Unbounded => panic!("iteration using unbounded range start without domain minimum impossible"),
            },
            end: self.end,
        }
    }
}

/// A stateful `Iterator` that acts like a state machine, generating the next value in a `GenericRange<T>`.
impl<T> Iterator for GenericIterator<T>
where
    T: Iterable<Output = T>,
{
    type Item = T;

    fn next(&mut self) -> Option<T> {
        match self.current.take() {
            None => None,
            Some(val) => {
                self.current = val.next();

                match self.end {
                    Bound::Included(ref end) => {
                        // we have to iterate until `val` is equal to `end`
                        if val == *end {
                            self.current = None;
                        }
                    }
                    Bound::Excluded(ref end) => {
                        // when `val` equals `end` the previous item was the last in the range
                        if val == *end {
                            self.current = None;
                            return None;
                        }
                    }
                    Bound::Unbounded => (),
                }

                Some(val)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use alloc::vec;
    use alloc::vec::Vec;

    use crate::GenericRange;

    #[test]
    fn iter_single() {
        let test_range = GenericRange::from(0_u8..1_u8);
        assert_eq!(test_range.into_iter().collect::<Vec<_>>(), vec![0]);
    }

    #[test]
    fn iter_empty() {
        let test_range = GenericRange::from(0_u8..0_u8);
        assert_eq!(test_range.into_iter().collect::<Vec<_>>(), vec![]);
    }

    #[test]
    fn iter_excluding_start() {
        let test_range = GenericRange::new_open_closed(11_u8, 13_u8);
        assert_eq!(test_range.into_iter().collect::<Vec<_>>(), vec![12, 13]);
    }

    #[test]
    fn iter_excluding_start_empty() {
        let test_range = GenericRange::new_open_closed(255_u8, 255_u8);
        assert_eq!(test_range.into_iter().collect::<Vec<_>>(), vec![]);
    }

    #[test]
    fn char_skip_bound_low() {
        let test_range = GenericRange::from('\u{d7ff}'..);
        let mut iter = test_range.into_iter();
        assert_eq!(iter.next().unwrap(), '\u{d7ff}');
        assert_eq!(iter.next().unwrap(), '\u{e000}');
    }

    #[test]
    fn char_bound_high() {
        let test_range = GenericRange::from('\u{e000}'..);
        assert_eq!(test_range.into_iter().last().unwrap(), '\u{10FFFF}');
    }
}