use crate::RangeStart;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum RangeEnd<Idx> {
Inclusive(Idx),
Exclusive(Idx),
}
const fn index<Idx>(end: &RangeEnd<Idx>) -> &Idx {
match end {
RangeEnd::Inclusive(index) | RangeEnd::Exclusive(index) => index,
}
}
const fn rank<Idx>(end: &RangeEnd<Idx>) -> u8 {
match end {
RangeEnd::Exclusive(_) => 0,
RangeEnd::Inclusive(_) => 1,
}
}
impl<Idx: Ord> Ord for RangeEnd<Idx> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
index(self)
.cmp(index(other))
.then_with(|| rank(self).cmp(&rank(other)))
}
}
impl<Idx: PartialOrd> PartialOrd for RangeEnd<Idx> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
index(self)
.partial_cmp(index(other))
.map(|ordering| ordering.then_with(|| rank(self).cmp(&rank(other))))
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Range<Idx> {
pub start: Idx,
pub end: RangeEnd<Idx>,
}
impl<Idx> Range<Idx> {
#[inline]
#[must_use]
pub const fn new(start: Idx, end: RangeEnd<Idx>) -> Self {
Self { start, end }
}
#[inline]
#[must_use]
pub const fn inclusive(start: Idx, end: Idx) -> Self {
Self::new(start, RangeEnd::Inclusive(end))
}
#[inline]
#[must_use]
pub const fn exclusive(start: Idx, end: Idx) -> Self {
Self::new(start, RangeEnd::Exclusive(end))
}
}
impl<Idx: Clone> RangeStart<Idx> for Range<Idx> {
#[inline]
fn start(&self) -> Idx {
self.start.clone()
}
}
impl<Idx: Clone + Ord> crate::PartialRangeExt<Idx> for Range<Idx> {
#[inline]
fn end_bound(&self) -> Option<RangeEnd<Idx>> {
Some(self.end.clone())
}
}
impl<Idx> From<std::ops::Range<Idx>> for Range<Idx> {
#[inline]
fn from(value: std::ops::Range<Idx>) -> Self {
Self::exclusive(value.start, value.end)
}
}
impl<Idx: Clone + PartialOrd> From<std::ops::RangeInclusive<Idx>> for Range<Idx> {
#[inline]
fn from(value: std::ops::RangeInclusive<Idx>) -> Self {
let empty = value.is_empty();
let (start, end) = value.into_inner();
if empty {
Self::exclusive(start.clone(), start)
} else {
Self::inclusive(start, end)
}
}
}