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    #[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}