ps-range 0.1.0-3

generalized range trait
Documentation
use std::ops::{self, RangeToInclusive};

use num_traits::{One, SaturatingAdd, Zero};

#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct OpenRange<Idx> {
    pub start: Idx,
    pub end: Option<Idx>,
}

pub trait PartialRange<Idx: Clone + Ord = usize> {
    #[must_use]
    fn start(&self) -> Idx;

    #[must_use]
    fn end(&self) -> Option<Idx>;

    #[inline]
    #[must_use]
    fn clamp(&self, start: impl Into<Idx>, end: impl Into<Idx>) -> ops::Range<Idx> {
        let lhs_start = start.into();
        let lhs_end = end.into();
        let lhs = lhs_start..lhs_end.clone();

        let rhs_start = self.start();
        let rhs_end = self.end().unwrap_or(lhs_end);
        let rhs = rhs_start..rhs_end;

        Range::intersection(&lhs, &rhs)
    }

    #[inline]
    #[must_use]
    fn clamp_left(&self, start: impl Into<Idx>) -> OpenRange<Idx> {
        OpenRange {
            start: self.start().max(start.into()),
            end: self.end(),
        }
    }

    #[inline]
    #[must_use]
    fn clamp_right(&self, end: impl Into<Idx>) -> ops::Range<Idx> {
        let end = match self.end() {
            Some(rhs_end) => end.into().min(rhs_end),
            None => end.into(),
        };

        let start = self.start().min(end.clone());

        start..end
    }

    #[inline]
    #[must_use]
    fn intersection<T, R>(&self, other: R) -> OpenRange<Idx>
    where
        T: Clone + Ord + Into<Idx>,
        R: PartialRange<T>,
    {
        other.end().map_or_else(
            || self.clamp_left(other.start()),
            |end| self.clamp(other.start(), end).to_open_range(),
        )
    }
}

impl<Idx: Clone + Ord> PartialRange<Idx> for OpenRange<Idx> {
    #[inline]
    fn start(&self) -> Idx {
        self.start.clone()
    }

    #[inline]
    fn end(&self) -> Option<Idx> {
        self.end.clone()
    }
}

pub trait Range<Idx: Clone + Ord = usize> {
    fn start(&self) -> Idx;
    fn end(&self) -> Idx;

    #[inline]
    #[must_use]
    fn clamp(&self, start: impl Into<Idx>, end: impl Into<Idx>) -> ops::Range<Idx> {
        let end = self.end().min(end.into());
        let start = self.start().max(start.into()).min(end.clone());

        start..end
    }

    #[inline]
    #[must_use]
    fn clamp_left(&self, start: impl Into<Idx>) -> ops::Range<Idx> {
        self.clamp(start, self.end())
    }

    #[inline]
    #[must_use]
    fn clamp_right(&self, end: impl Into<Idx>) -> ops::Range<Idx> {
        self.clamp(self.start(), end)
    }

    #[inline]
    #[must_use]
    fn intersection<T, R>(&self, other: R) -> ops::Range<Idx>
    where
        T: Clone + Ord + Into<Idx>,
        R: Range<T>,
    {
        self.clamp(other.start(), other.end())
    }

    #[inline]
    #[must_use]
    fn to_open_range(&self) -> OpenRange<Idx> {
        OpenRange {
            start: self.start(),
            end: Some(self.end()),
        }
    }
}

impl<Idx: Clone + Ord> Range<Idx> for ops::Range<Idx> {
    fn start(&self) -> Idx {
        self.start.clone()
    }

    fn end(&self) -> Idx {
        self.end.clone()
    }
}

impl<Idx: Clone + Ord> PartialRange<Idx> for ops::Range<Idx> {
    fn start(&self) -> Idx {
        self.start.clone()
    }

    fn end(&self) -> Option<Idx> {
        Some(self.end.clone())
    }
}

impl<Idx: Clone + Ord> PartialRange<Idx> for ops::RangeFrom<Idx> {
    fn start(&self) -> Idx {
        self.start.clone()
    }

    fn end(&self) -> Option<Idx> {
        None
    }
}

impl<Idx: Clone + Ord + Zero> PartialRange<Idx> for ops::RangeFull {
    fn start(&self) -> Idx {
        Zero::zero()
    }

    fn end(&self) -> Option<Idx> {
        None
    }
}

impl<Idx: Copy + One + Ord + SaturatingAdd> Range<Idx> for ops::RangeInclusive<Idx> {
    fn start(&self) -> Idx {
        *self.start()
    }

    fn end(&self) -> Idx {
        self.end().saturating_add(&One::one())
    }
}

impl<Idx: Copy + One + Ord + SaturatingAdd> PartialRange<Idx> for ops::RangeInclusive<Idx> {
    fn start(&self) -> Idx {
        *self.start()
    }

    fn end(&self) -> Option<Idx> {
        Some(self.end().saturating_add(&One::one()))
    }
}

impl<Idx: Copy + Ord + Zero> Range<Idx> for ops::RangeTo<Idx> {
    fn start(&self) -> Idx {
        Zero::zero()
    }

    fn end(&self) -> Idx {
        self.end
    }
}

impl<Idx: Copy + Ord + Zero> PartialRange<Idx> for ops::RangeTo<Idx> {
    fn start(&self) -> Idx {
        Zero::zero()
    }

    fn end(&self) -> Option<Idx> {
        Some(self.end)
    }
}

impl<Idx: Clone + One + Ord + SaturatingAdd + Zero> Range<Idx> for RangeToInclusive<Idx> {
    fn start(&self) -> Idx {
        Zero::zero()
    }

    fn end(&self) -> Idx {
        self.end.saturating_add(&One::one())
    }
}

impl<Idx: Clone + One + Ord + SaturatingAdd + Zero> PartialRange<Idx> for RangeToInclusive<Idx> {
    fn start(&self) -> Idx {
        Zero::zero()
    }

    fn end(&self) -> Option<Idx> {
        Some(self.end.saturating_add(&One::one()))
    }
}

impl<Idx: Clone + Ord, T: PartialRange<Idx>> PartialRange<Idx> for &T {
    fn start(&self) -> Idx {
        (*self).start()
    }

    fn end(&self) -> Option<Idx> {
        (*self).end()
    }
}

impl<Idx: Clone + Ord, T: Range<Idx>> Range<Idx> for &T {
    fn start(&self) -> Idx {
        (*self).start()
    }

    fn end(&self) -> Idx {
        (*self).end()
    }
}