use num_traits::{CheckedAdd, One, SaturatingSub};
use crate::{Range, RangeEnd};
pub trait RangeExt<Idx = usize>: crate::RangeStart<Idx>
where
Idx: Clone + Ord,
{
#[must_use]
fn end_inclusive(&self) -> Idx;
#[must_use]
fn end_exclusive(&self) -> Option<Idx>;
#[inline]
#[must_use]
fn clamp_to(&self, start: impl Into<Idx>, end: impl Into<Idx>) -> std::ops::Range<Idx> {
let window_end = end.into();
let end = match self.end_exclusive() {
Some(end) => end.min(window_end),
None => window_end,
};
let start = self.start().max(start.into()).min(end.clone());
start..end
}
#[inline]
#[must_use]
fn clamp_right(&self, end: impl Into<Idx>) -> std::ops::Range<Idx> {
self.clamp_to(self.start(), end)
}
}
impl<Idx> RangeExt<Idx> for std::ops::Range<Idx>
where
Idx: Clone + Ord + One + SaturatingSub,
{
#[inline]
fn end_inclusive(&self) -> Idx {
self.end.saturating_sub(&Idx::one())
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
Some(self.end.clone())
}
}
impl<Idx> RangeExt<Idx> for std::ops::RangeInclusive<Idx>
where
Idx: Clone + Ord + One + CheckedAdd,
{
#[inline]
fn end_inclusive(&self) -> Idx {
self.end().clone()
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
if self.is_empty() {
Some(self.start().clone())
} else {
self.end().checked_add(&Idx::one())
}
}
}
impl<Idx> RangeExt<Idx> for std::ops::RangeTo<Idx>
where
Idx: Clone + Ord + num_traits::Zero + One + SaturatingSub,
{
#[inline]
fn end_inclusive(&self) -> Idx {
self.end.saturating_sub(&Idx::one())
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
Some(self.end.clone())
}
}
impl<Idx> RangeExt<Idx> for std::ops::RangeToInclusive<Idx>
where
Idx: Clone + Ord + num_traits::Zero + One + CheckedAdd,
{
#[inline]
fn end_inclusive(&self) -> Idx {
self.end.clone()
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
self.end.checked_add(&Idx::one())
}
}
impl<Idx> RangeExt<Idx> for Range<Idx>
where
Idx: Clone + Ord + One + CheckedAdd + SaturatingSub,
{
#[inline]
fn end_inclusive(&self) -> Idx {
match &self.end {
RangeEnd::Inclusive(end) => end.clone(),
RangeEnd::Exclusive(end) => end.saturating_sub(&Idx::one()),
}
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
match &self.end {
RangeEnd::Inclusive(end) => end.checked_add(&Idx::one()),
RangeEnd::Exclusive(end) => Some(end.clone()),
}
}
}
impl<Idx, T> RangeExt<Idx> for &T
where
Idx: Clone + Ord,
T: RangeExt<Idx>,
{
#[inline]
fn end_inclusive(&self) -> Idx {
(*self).end_inclusive()
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
(*self).end_exclusive()
}
}
impl<Idx, T> RangeExt<Idx> for &mut T
where
Idx: Clone + Ord,
T: RangeExt<Idx>,
{
#[inline]
fn end_inclusive(&self) -> Idx {
(**self).end_inclusive()
}
#[inline]
fn end_exclusive(&self) -> Option<Idx> {
(**self).end_exclusive()
}
}