1use std::ops::{self, RangeToInclusive};
2
3use num_traits::{One, SaturatingAdd, Zero};
4
5#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
6pub struct OpenRange<Idx> {
7 pub start: Idx,
8 pub end: Option<Idx>,
9}
10
11pub trait PartialRange<Idx: Clone + Ord = usize> {
12 #[must_use]
13 fn start(&self) -> Idx;
14
15 #[must_use]
16 fn end(&self) -> Option<Idx>;
17
18 #[inline]
19 #[must_use]
20 fn clamp(&self, start: impl Into<Idx>, end: impl Into<Idx>) -> ops::Range<Idx> {
21 let lhs_start = start.into();
22 let lhs_end = end.into();
23 let lhs = lhs_start..lhs_end.clone();
24
25 let rhs_start = self.start();
26 let rhs_end = self.end().unwrap_or(lhs_end);
27 let rhs = rhs_start..rhs_end;
28
29 Range::intersection(&lhs, &rhs)
30 }
31
32 #[inline]
33 #[must_use]
34 fn clamp_left(&self, start: impl Into<Idx>) -> OpenRange<Idx> {
35 OpenRange {
36 start: self.start().max(start.into()),
37 end: self.end(),
38 }
39 }
40
41 #[inline]
42 #[must_use]
43 fn clamp_right(&self, end: impl Into<Idx>) -> ops::Range<Idx> {
44 let end = match self.end() {
45 Some(rhs_end) => end.into().min(rhs_end),
46 None => end.into(),
47 };
48
49 let start = self.start().min(end.clone());
50
51 start..end
52 }
53
54 #[inline]
55 #[must_use]
56 fn intersection<T, R>(&self, other: R) -> OpenRange<Idx>
57 where
58 T: Clone + Ord + Into<Idx>,
59 R: PartialRange<T>,
60 {
61 other.end().map_or_else(
62 || self.clamp_left(other.start()),
63 |end| self.clamp(other.start(), end).to_open_range(),
64 )
65 }
66}
67
68impl<Idx: Clone + Ord> PartialRange<Idx> for OpenRange<Idx> {
69 #[inline]
70 #[must_use]
71 fn start(&self) -> Idx {
72 self.start.clone()
73 }
74
75 #[inline]
76 #[must_use]
77 fn end(&self) -> Option<Idx> {
78 self.end.clone()
79 }
80}
81
82pub trait Range<Idx: Clone + Ord = usize> {
83 fn start(&self) -> Idx;
84 fn end(&self) -> Idx;
85
86 #[inline]
87 #[must_use]
88 fn clamp(&self, start: impl Into<Idx>, end: impl Into<Idx>) -> ops::Range<Idx> {
89 let end = self.end().min(end.into());
90 let start = self.start().max(start.into()).min(end.clone());
91
92 start..end
93 }
94
95 #[inline]
96 #[must_use]
97 fn clamp_left(&self, start: impl Into<Idx>) -> ops::Range<Idx> {
98 self.clamp(start, self.end())
99 }
100
101 #[inline]
102 #[must_use]
103 fn clamp_right(&self, end: impl Into<Idx>) -> ops::Range<Idx> {
104 self.clamp(self.start(), end)
105 }
106
107 #[inline]
108 #[must_use]
109 fn intersection<T, R>(&self, other: &R) -> ops::Range<Idx>
110 where
111 T: Clone + Ord + Into<Idx>,
112 R: Range<T>,
113 {
114 self.clamp(other.start(), other.end())
115 }
116
117 #[inline]
118 #[must_use]
119 fn to_open_range(&self) -> OpenRange<Idx> {
120 OpenRange {
121 start: self.start(),
122 end: Some(self.end()),
123 }
124 }
125}
126
127impl<Idx: Clone + Ord> Range<Idx> for ops::Range<Idx> {
128 fn start(&self) -> Idx {
129 self.start.clone()
130 }
131
132 fn end(&self) -> Idx {
133 self.end.clone()
134 }
135}
136
137impl<Idx: Clone + Ord> PartialRange<Idx> for ops::Range<Idx> {
138 fn start(&self) -> Idx {
139 self.start.clone()
140 }
141
142 fn end(&self) -> Option<Idx> {
143 Some(self.end.clone())
144 }
145}
146
147impl<Idx: Clone + Ord> PartialRange<Idx> for ops::RangeFrom<Idx> {
148 fn start(&self) -> Idx {
149 self.start.clone()
150 }
151
152 fn end(&self) -> Option<Idx> {
153 None
154 }
155}
156
157impl<Idx: Clone + Ord + Zero> PartialRange<Idx> for ops::RangeFull {
158 fn start(&self) -> Idx {
159 Zero::zero()
160 }
161
162 fn end(&self) -> Option<Idx> {
163 None
164 }
165}
166
167impl<Idx: Copy + One + Ord + SaturatingAdd> Range<Idx> for ops::RangeInclusive<Idx> {
168 fn start(&self) -> Idx {
169 *self.start()
170 }
171
172 fn end(&self) -> Idx {
173 self.end().clone().saturating_add(&One::one())
174 }
175}
176
177impl<Idx: Copy + One + Ord + SaturatingAdd> PartialRange<Idx> for ops::RangeInclusive<Idx> {
178 fn start(&self) -> Idx {
179 *self.start()
180 }
181
182 fn end(&self) -> Option<Idx> {
183 Some(self.end().clone().saturating_add(&One::one()))
184 }
185}
186
187impl<Idx: Copy + Ord + Zero> Range<Idx> for ops::RangeTo<Idx> {
188 fn start(&self) -> Idx {
189 Zero::zero()
190 }
191
192 fn end(&self) -> Idx {
193 self.end
194 }
195}
196
197impl<Idx: Copy + Ord + Zero> PartialRange<Idx> for ops::RangeTo<Idx> {
198 fn start(&self) -> Idx {
199 Zero::zero()
200 }
201
202 fn end(&self) -> Option<Idx> {
203 Some(self.end)
204 }
205}
206
207impl<Idx: Clone + One + Ord + SaturatingAdd + Zero> Range<Idx> for RangeToInclusive<Idx> {
208 fn start(&self) -> Idx {
209 Zero::zero()
210 }
211
212 fn end(&self) -> Idx {
213 self.end.saturating_add(&One::one())
214 }
215}
216
217impl<Idx: Clone + One + Ord + SaturatingAdd + Zero> PartialRange<Idx> for RangeToInclusive<Idx> {
218 fn start(&self) -> Idx {
219 Zero::zero()
220 }
221
222 fn end(&self) -> Option<Idx> {
223 Some(self.end.saturating_add(&One::one()))
224 }
225}