1use crate::RangeStart;
2
3#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
20pub enum RangeEnd<Idx> {
21 Inclusive(Idx),
23 Exclusive(Idx),
25}
26
27const fn index<Idx>(end: &RangeEnd<Idx>) -> &Idx {
28 match end {
29 RangeEnd::Inclusive(index) | RangeEnd::Exclusive(index) => index,
30 }
31}
32
33const fn rank<Idx>(end: &RangeEnd<Idx>) -> u8 {
34 match end {
35 RangeEnd::Exclusive(_) => 0,
36 RangeEnd::Inclusive(_) => 1,
37 }
38}
39
40impl<Idx: Ord> Ord for RangeEnd<Idx> {
41 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
42 index(self)
43 .cmp(index(other))
44 .then_with(|| rank(self).cmp(&rank(other)))
45 }
46}
47
48impl<Idx: PartialOrd> PartialOrd for RangeEnd<Idx> {
49 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
50 index(self)
51 .partial_cmp(index(other))
52 .map(|ordering| ordering.then_with(|| rank(self).cmp(&rank(other))))
53 }
54}
55
56#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
70pub struct Range<Idx> {
71 pub start: Idx,
73 pub end: RangeEnd<Idx>,
75}
76
77impl<Idx> Range<Idx> {
78 #[inline]
80 #[must_use]
81 pub const fn new(start: Idx, end: RangeEnd<Idx>) -> Self {
82 Self { start, end }
83 }
84
85 #[inline]
87 #[must_use]
88 pub const fn inclusive(start: Idx, end: Idx) -> Self {
89 Self::new(start, RangeEnd::Inclusive(end))
90 }
91
92 #[inline]
94 #[must_use]
95 pub const fn exclusive(start: Idx, end: Idx) -> Self {
96 Self::new(start, RangeEnd::Exclusive(end))
97 }
98}
99
100impl<Idx: Clone> RangeStart<Idx> for Range<Idx> {
101 #[inline]
102 fn start(&self) -> Idx {
103 self.start.clone()
104 }
105}
106
107impl<Idx: Clone + Ord> crate::PartialRangeExt<Idx> for Range<Idx> {
108 #[inline]
109 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
110 Some(self.end.clone())
111 }
112}
113
114impl<Idx> From<std::ops::Range<Idx>> for Range<Idx> {
115 #[inline]
116 fn from(value: std::ops::Range<Idx>) -> Self {
117 Self::exclusive(value.start, value.end)
118 }
119}
120
121impl<Idx: Clone + PartialOrd> From<std::ops::RangeInclusive<Idx>> for Range<Idx> {
127 #[inline]
128 fn from(value: std::ops::RangeInclusive<Idx>) -> Self {
129 let empty = value.is_empty();
130 let (start, end) = value.into_inner();
131
132 if empty {
133 Self::exclusive(start.clone(), start)
134 } else {
135 Self::inclusive(start, end)
136 }
137 }
138}