ps_range/
lib.rs

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    fn start(&self) -> Idx {
71        self.start.clone()
72    }
73
74    #[inline]
75    fn end(&self) -> Option<Idx> {
76        self.end.clone()
77    }
78}
79
80pub trait Range<Idx: Clone + Ord = usize> {
81    fn start(&self) -> Idx;
82    fn end(&self) -> Idx;
83
84    #[inline]
85    #[must_use]
86    fn clamp(&self, start: impl Into<Idx>, end: impl Into<Idx>) -> ops::Range<Idx> {
87        let end = self.end().min(end.into());
88        let start = self.start().max(start.into()).min(end.clone());
89
90        start..end
91    }
92
93    #[inline]
94    #[must_use]
95    fn clamp_left(&self, start: impl Into<Idx>) -> ops::Range<Idx> {
96        self.clamp(start, self.end())
97    }
98
99    #[inline]
100    #[must_use]
101    fn clamp_right(&self, end: impl Into<Idx>) -> ops::Range<Idx> {
102        self.clamp(self.start(), end)
103    }
104
105    #[inline]
106    #[must_use]
107    fn intersection<T, R>(&self, other: R) -> ops::Range<Idx>
108    where
109        T: Clone + Ord + Into<Idx>,
110        R: Range<T>,
111    {
112        self.clamp(other.start(), other.end())
113    }
114
115    #[inline]
116    #[must_use]
117    fn to_open_range(&self) -> OpenRange<Idx> {
118        OpenRange {
119            start: self.start(),
120            end: Some(self.end()),
121        }
122    }
123}
124
125impl<Idx: Clone + Ord> Range<Idx> for ops::Range<Idx> {
126    fn start(&self) -> Idx {
127        self.start.clone()
128    }
129
130    fn end(&self) -> Idx {
131        self.end.clone()
132    }
133}
134
135impl<Idx: Clone + Ord> PartialRange<Idx> for ops::Range<Idx> {
136    fn start(&self) -> Idx {
137        self.start.clone()
138    }
139
140    fn end(&self) -> Option<Idx> {
141        Some(self.end.clone())
142    }
143}
144
145impl<Idx: Clone + Ord> PartialRange<Idx> for ops::RangeFrom<Idx> {
146    fn start(&self) -> Idx {
147        self.start.clone()
148    }
149
150    fn end(&self) -> Option<Idx> {
151        None
152    }
153}
154
155impl<Idx: Clone + Ord + Zero> PartialRange<Idx> for ops::RangeFull {
156    fn start(&self) -> Idx {
157        Zero::zero()
158    }
159
160    fn end(&self) -> Option<Idx> {
161        None
162    }
163}
164
165impl<Idx: Copy + One + Ord + SaturatingAdd> Range<Idx> for ops::RangeInclusive<Idx> {
166    fn start(&self) -> Idx {
167        *self.start()
168    }
169
170    fn end(&self) -> Idx {
171        self.end().saturating_add(&One::one())
172    }
173}
174
175impl<Idx: Copy + One + Ord + SaturatingAdd> PartialRange<Idx> for ops::RangeInclusive<Idx> {
176    fn start(&self) -> Idx {
177        *self.start()
178    }
179
180    fn end(&self) -> Option<Idx> {
181        Some(self.end().saturating_add(&One::one()))
182    }
183}
184
185impl<Idx: Copy + Ord + Zero> Range<Idx> for ops::RangeTo<Idx> {
186    fn start(&self) -> Idx {
187        Zero::zero()
188    }
189
190    fn end(&self) -> Idx {
191        self.end
192    }
193}
194
195impl<Idx: Copy + Ord + Zero> PartialRange<Idx> for ops::RangeTo<Idx> {
196    fn start(&self) -> Idx {
197        Zero::zero()
198    }
199
200    fn end(&self) -> Option<Idx> {
201        Some(self.end)
202    }
203}
204
205impl<Idx: Clone + One + Ord + SaturatingAdd + Zero> Range<Idx> for RangeToInclusive<Idx> {
206    fn start(&self) -> Idx {
207        Zero::zero()
208    }
209
210    fn end(&self) -> Idx {
211        self.end.saturating_add(&One::one())
212    }
213}
214
215impl<Idx: Clone + One + Ord + SaturatingAdd + Zero> PartialRange<Idx> for RangeToInclusive<Idx> {
216    fn start(&self) -> Idx {
217        Zero::zero()
218    }
219
220    fn end(&self) -> Option<Idx> {
221        Some(self.end.saturating_add(&One::one()))
222    }
223}
224
225impl<Idx: Clone + Ord, T: PartialRange<Idx>> PartialRange<Idx> for &T {
226    fn start(&self) -> Idx {
227        (*self).start()
228    }
229
230    fn end(&self) -> Option<Idx> {
231        (*self).end()
232    }
233}
234
235impl<Idx: Clone + Ord, T: Range<Idx>> Range<Idx> for &T {
236    fn start(&self) -> Idx {
237        (*self).start()
238    }
239
240    fn end(&self) -> Idx {
241        (*self).end()
242    }
243}