ps_range/
partial_range_ext.rs1use num_traits::{One, Zero};
2
3use crate::{PartialRange, Range, RangeEnd, RangeStart};
4
5pub trait PartialRangeExt<Idx = usize>: RangeStart<Idx>
44where
45 Idx: Clone + Ord,
46{
47 #[inline]
49 #[must_use]
50 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
51 None
52 }
53
54 #[inline]
56 #[must_use]
57 fn is_empty(&self) -> bool {
58 match self.end_bound() {
59 Some(RangeEnd::Exclusive(end)) => end <= self.start(),
60 Some(RangeEnd::Inclusive(end)) => end < self.start(),
61 None => false,
62 }
63 }
64
65 #[inline]
72 #[must_use]
73 fn clamp_exclusive(
74 &self,
75 start: impl Into<Idx>,
76 exclusive_end: impl Into<Idx>,
77 ) -> std::ops::Range<Idx>
78 where
79 Idx: std::ops::Add<Output = Idx> + One,
80 {
81 let window_start = start.into();
82 let window_end = exclusive_end.into();
83
84 if self.is_empty() {
85 let anchor = self.start().max(window_start).min(window_end);
86
87 return anchor.clone()..anchor;
88 }
89
90 let end = match self.end_bound() {
91 None => window_end,
92 Some(RangeEnd::Exclusive(end)) => end.min(window_end),
93 Some(RangeEnd::Inclusive(end)) => {
94 if end < window_end {
97 end + Idx::one()
98 } else {
99 window_end
100 }
101 }
102 };
103 let start = self.start().max(window_start).min(end.clone());
104
105 start..end
106 }
107
108 #[inline]
115 #[must_use]
116 fn clamp_inclusive(&self, start: impl Into<Idx>, inclusive_end: impl Into<Idx>) -> Range<Idx> {
117 let window_start = start.into();
118 let window_end = inclusive_end.into();
119
120 if self.is_empty() {
121 let anchor = self.start().max(window_start).min(window_end);
122
123 return Range::exclusive(anchor.clone(), anchor);
124 }
125
126 let end = match self.end_bound() {
127 None => RangeEnd::Inclusive(window_end),
128 Some(end) => end.min(RangeEnd::Inclusive(window_end)),
129 };
130 let start = self.start().max(window_start);
131
132 match end {
133 RangeEnd::Inclusive(end) if start <= end => Range::inclusive(start, end),
134 RangeEnd::Exclusive(end) if start < end => Range::exclusive(start, end),
135 RangeEnd::Inclusive(end) | RangeEnd::Exclusive(end) => {
136 Range::exclusive(end.clone(), end)
137 }
138 }
139 }
140
141 #[inline]
143 #[must_use]
144 fn clamp_right_inclusive(&self, inclusive_end: impl Into<Idx>) -> Range<Idx> {
145 self.clamp_inclusive(self.start(), inclusive_end)
146 }
147
148 #[inline]
150 #[must_use]
151 fn clamp_right_exclusive(&self, exclusive_end: impl Into<Idx>) -> std::ops::Range<Idx>
152 where
153 Idx: std::ops::Add<Output = Idx> + One,
154 {
155 self.clamp_exclusive(self.start(), exclusive_end)
156 }
157
158 #[inline]
165 #[must_use]
166 fn clamp_left(&self, start: impl Into<Idx>) -> PartialRange<Idx> {
167 let start = self.start().max(start.into());
168
169 if self.is_empty() {
170 return PartialRange::Empty { idx: start };
171 }
172
173 match self.end_bound() {
174 None => PartialRange::From { inner: start.. },
175 Some(RangeEnd::Inclusive(end)) if start <= end => {
176 PartialRange::Inclusive { start, end }
177 }
178 Some(RangeEnd::Exclusive(end)) if start < end => {
179 PartialRange::Exclusive { inner: start..end }
180 }
181 Some(_) => PartialRange::Empty { idx: start },
182 }
183 }
184
185 #[inline]
191 #[must_use]
192 fn intersection<R>(&self, other: &R) -> PartialRange<Idx>
193 where
194 R: PartialRangeExt<Idx>,
195 {
196 let start = self.start().max(other.start());
197
198 if self.is_empty() || other.is_empty() {
199 return PartialRange::Empty { idx: start };
200 }
201
202 let end = match (self.end_bound(), other.end_bound()) {
203 (None, None) => return PartialRange::From { inner: start.. },
204 (Some(end), None) | (None, Some(end)) => end,
205 (Some(lhs), Some(rhs)) => lhs.min(rhs),
206 };
207
208 match end {
209 RangeEnd::Inclusive(end) if start <= end => PartialRange::Inclusive { start, end },
210 RangeEnd::Exclusive(end) if start < end => {
211 PartialRange::Exclusive { inner: start..end }
212 }
213 _ => PartialRange::Empty { idx: start },
214 }
215 }
216}
217
218impl<Idx: Clone + Ord> PartialRangeExt<Idx> for std::ops::Range<Idx> {
219 #[inline]
220 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
221 Some(RangeEnd::Exclusive(self.end.clone()))
222 }
223}
224
225impl<Idx: Clone + Ord> PartialRangeExt<Idx> for std::ops::RangeFrom<Idx> {}
226
227impl<Idx: Clone + Ord + Zero> PartialRangeExt<Idx> for std::ops::RangeFull {}
228
229impl<Idx: Clone + Ord> PartialRangeExt<Idx> for std::ops::RangeInclusive<Idx> {
230 #[inline]
231 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
232 Some(RangeEnd::Inclusive(self.end().clone()))
233 }
234
235 #[inline]
236 fn is_empty(&self) -> bool {
237 Self::is_empty(self)
238 }
239}
240
241impl<Idx: Clone + Ord + Zero> PartialRangeExt<Idx> for std::ops::RangeTo<Idx> {
242 #[inline]
243 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
244 Some(RangeEnd::Exclusive(self.end.clone()))
245 }
246}
247
248impl<Idx: Clone + Ord + Zero> PartialRangeExt<Idx> for std::ops::RangeToInclusive<Idx> {
249 #[inline]
250 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
251 Some(RangeEnd::Inclusive(self.end.clone()))
252 }
253}
254
255impl<Idx, T> PartialRangeExt<Idx> for Option<T>
258where
259 Idx: Clone + Ord + Zero,
260 T: PartialRangeExt<Idx>,
261{
262 #[inline]
263 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
264 self.as_ref().map_or_else(
265 || Some(RangeEnd::Exclusive(Idx::zero())),
266 PartialRangeExt::end_bound,
267 )
268 }
269
270 #[inline]
271 fn is_empty(&self) -> bool {
272 self.as_ref().is_none_or(PartialRangeExt::is_empty)
273 }
274}
275
276impl<Idx, T> PartialRangeExt<Idx> for &T
277where
278 Idx: Clone + Ord,
279 T: PartialRangeExt<Idx>,
280{
281 #[inline]
282 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
283 (*self).end_bound()
284 }
285
286 #[inline]
287 fn is_empty(&self) -> bool {
288 (*self).is_empty()
289 }
290}
291
292impl<Idx, T> PartialRangeExt<Idx> for &mut T
293where
294 Idx: Clone + Ord,
295 T: PartialRangeExt<Idx>,
296{
297 #[inline]
298 fn end_bound(&self) -> Option<RangeEnd<Idx>> {
299 (**self).end_bound()
300 }
301
302 #[inline]
303 fn is_empty(&self) -> bool {
304 (**self).is_empty()
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::PartialRangeExt;
311 use crate::PartialRange;
312
313 struct StartOnlyRange;
314
315 impl crate::RangeStart<usize> for StartOnlyRange {
316 fn start(&self) -> usize {
317 42
318 }
319 }
320
321 impl PartialRangeExt<usize> for StartOnlyRange {}
322
323 #[test]
324 fn a_start_only_implementor_is_unbounded_above() {
325 let range = StartOnlyRange;
326
327 assert_eq!(range.end_bound(), None);
328 assert!(!range.is_empty());
329 assert_eq!(range.clamp_right_exclusive(50usize), 42..50);
330 assert_eq!(
331 range.clamp_left(50usize),
332 PartialRange::From { inner: 50.. }
333 );
334 }
335}