ranges 0.4.0

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::GenericRange;

impl<T> GenericRange<T> {
    /// Returns true if the given `item` is contained in the range.
    ///
    /// # Example
    ///```
    /// use ranges::GenericRange;
    ///
    /// let range = GenericRange::from(1..=5);
    /// assert!(range.contains_item(&3));
    ///```
    #[must_use]
    pub fn contains_item<U>(&self, item: &U) -> bool
    where
        T: PartialOrd<U>,
        U: ?Sized + PartialOrd<T>,
    {
        Self::generic_start_end_contains(&self.start, &self.end, item)
    }

    /// Returns true if the given `item` is contained between the start and end.
    pub(crate) fn generic_start_end_contains<U>(start: &Bound<T>, end: &Bound<T>, item: &U) -> bool
    where
        T: PartialOrd<U>,
        U: ?Sized + PartialOrd<T>,
    {
        #[allow(clippy::needless_borrowed_reference)]
        match (start, end) {
            (&Bound::Unbounded, &Bound::Unbounded) => true,

            (&Bound::Unbounded, &Bound::Excluded(ref y)) => item < y,
            (&Bound::Unbounded, &Bound::Included(ref y)) => item <= y,

            (&Bound::Excluded(ref x), &Bound::Unbounded) => item > x,
            (&Bound::Included(ref x), &Bound::Unbounded) => item >= x,

            (&Bound::Excluded(ref x), &Bound::Excluded(ref y)) => item > x && item < y,
            (&Bound::Excluded(ref x), &Bound::Included(ref y)) => item > x && item <= y,
            (&Bound::Included(ref x), &Bound::Excluded(ref y)) => item >= x && item < y,
            (&Bound::Included(ref x), &Bound::Included(ref y)) => item >= x && item <= y,
        }
    }
}

#[cfg(test)]
mod tests {
    use core::ops::Bound;

    use crate::GenericRange;

    #[test]
    fn in_ex() {
        assert!(!GenericRange::from(1..1).contains_item(&1));
        assert!(GenericRange::from(1..2).contains_item(&1));
        assert!(!GenericRange::from(1..2).contains_item(&2));
    }

    #[test]
    fn in_in() {
        assert!(GenericRange::from(1..=1).contains_item(&1));
        assert!(GenericRange::from(1..=2).contains_item(&1));
        assert!(GenericRange::from(1..=2).contains_item(&2));
    }

    #[test]
    fn in_unbound() {
        assert!(GenericRange::from(1..).contains_item(&1));
        assert!(GenericRange::from(1..).contains_item(&2));
    }

    #[test]
    fn unbound_ex() {
        assert!(GenericRange::from(..2).contains_item(&1));
        assert!(!GenericRange::from(..2).contains_item(&2));
    }

    #[test]
    fn unbound_in() {
        assert!(GenericRange::from(..=2).contains_item(&1));
        assert!(GenericRange::from(..=2).contains_item(&2));
    }

    #[test]
    fn unbound() {
        let generic: GenericRange<usize> = GenericRange::from(..);
        assert!(generic.contains_item(&1));
        assert!(generic.contains_item(&2));
    }

    #[test]
    fn ex_ex() {
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(1))).contains_item(&1));
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(2))).contains_item(&1));
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Excluded(2))).contains_item(&2));
    }

    #[test]
    fn ex_in() {
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Included(1))).contains_item(&1));
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Included(2))).contains_item(&1));
        assert!(GenericRange::from((Bound::Excluded(1), Bound::Included(2))).contains_item(&2));
    }

    #[test]
    fn ex_unbound() {
        assert!(!GenericRange::from((Bound::Excluded(1), Bound::Unbounded)).contains_item(&1));
        assert!(GenericRange::from((Bound::Excluded(1), Bound::Unbounded)).contains_item(&2));
    }
}

#[allow(clippy::tests_outside_test_module)]
#[cfg(all(test, feature = "noisy_float"))]
mod tests_continuous {
    use core::ops::Bound;

    use noisy_float::types::N64;

    use crate::GenericRange;

    #[test]
    fn in_ex() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(!GenericRange::from(one..one).contains_item(&one));
        assert!(GenericRange::from(one..two).contains_item(&one));
        assert!(GenericRange::from(one..two).contains_item(&one_half));
        assert!(!GenericRange::from(one..two).contains_item(&two));
    }

    #[test]
    fn in_in() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(GenericRange::from(one..=one).contains_item(&one));
        assert!(GenericRange::from(one..=two).contains_item(&one));
        assert!(GenericRange::from(one..=two).contains_item(&one_half));
        assert!(GenericRange::from(one..=two).contains_item(&two));
    }

    #[test]
    fn in_unbound() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(GenericRange::from(one..).contains_item(&one));
        assert!(GenericRange::from(one..).contains_item(&one_half));
        assert!(GenericRange::from(one..).contains_item(&two));
    }

    #[test]
    fn unbound_ex() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(GenericRange::from(..two).contains_item(&one));
        assert!(GenericRange::from(..two).contains_item(&one_half));
        assert!(!GenericRange::from(..two).contains_item(&two));
    }

    #[test]
    fn unbound_in() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(GenericRange::from(..=two).contains_item(&one));
        assert!(GenericRange::from(..=two).contains_item(&one_half));
        assert!(GenericRange::from(..=two).contains_item(&two));
    }

    #[test]
    fn unbound() {
        let one = N64::new(1.);
        let two = N64::new(2.);

        let generic: GenericRange<N64> = GenericRange::from(..);
        assert!(generic.contains_item(&one));
        assert!(generic.contains_item(&two));
    }

    #[test]
    fn ex_ex() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(!GenericRange::from((Bound::Excluded(one), Bound::Excluded(one))).contains_item(&one));
        assert!(!GenericRange::from((Bound::Excluded(one), Bound::Excluded(two))).contains_item(&one));
        assert!(GenericRange::from((Bound::Excluded(one), Bound::Excluded(two))).contains_item(&one_half));
        assert!(!GenericRange::from((Bound::Excluded(one), Bound::Excluded(two))).contains_item(&two));
    }

    #[test]
    fn ex_in() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(!GenericRange::from((Bound::Excluded(one), Bound::Included(one))).contains_item(&one));
        assert!(!GenericRange::from((Bound::Excluded(one), Bound::Included(two))).contains_item(&one));
        assert!(GenericRange::from((Bound::Excluded(one), Bound::Included(two))).contains_item(&one_half));
        assert!(GenericRange::from((Bound::Excluded(one), Bound::Included(two))).contains_item(&two));
    }

    #[test]
    fn ex_unbound() {
        let one = N64::new(1.);
        let one_half = N64::new(1.5);
        let two = N64::new(2.);

        assert!(!GenericRange::from((Bound::Excluded(one), Bound::Unbounded)).contains_item(&one));
        assert!(GenericRange::from((Bound::Excluded(one), Bound::Unbounded)).contains_item(&one_half));
        assert!(GenericRange::from((Bound::Excluded(one), Bound::Unbounded)).contains_item(&two));
    }
}