clap_builder/builder/
range.rs1#[derive(Copy, Clone, PartialEq, Eq, Hash)]
3pub struct ValueRange {
4 start_inclusive: usize,
5 end_inclusive: usize,
6}
7
8impl ValueRange {
9 pub const EMPTY: Self = Self {
11 start_inclusive: 0,
12 end_inclusive: 0,
13 };
14
15 pub const SINGLE: Self = Self {
17 start_inclusive: 1,
18 end_inclusive: 1,
19 };
20
21 #[cfg(debug_assertions)]
22 pub(crate) const OPTIONAL: Self = Self {
23 start_inclusive: 0,
24 end_inclusive: 1,
25 };
26
27 pub(crate) const FULL: Self = Self {
28 start_inclusive: 0,
29 end_inclusive: usize::MAX,
30 };
31
32 pub fn new(range: impl Into<Self>) -> Self {
65 range.into()
66 }
67
68 pub(crate) fn raw(start_inclusive: usize, end_inclusive: usize) -> Self {
69 debug_assert!(start_inclusive <= end_inclusive);
70 Self {
71 start_inclusive,
72 end_inclusive,
73 }
74 }
75
76 pub fn min_values(&self) -> usize {
78 self.start_inclusive
79 }
80
81 pub fn max_values(&self) -> usize {
83 self.end_inclusive
84 }
85
86 pub fn takes_values(&self) -> bool {
100 self.end_inclusive != 0
101 }
102
103 pub(crate) fn is_unbounded(&self) -> bool {
104 self.end_inclusive == usize::MAX
105 }
106
107 pub(crate) fn is_fixed(&self) -> bool {
108 self.start_inclusive == self.end_inclusive
109 }
110
111 pub(crate) fn is_multiple(&self) -> bool {
112 self.start_inclusive != self.end_inclusive || 1 < self.start_inclusive
113 }
114
115 pub(crate) fn num_values(&self) -> Option<usize> {
116 self.is_fixed().then_some(self.start_inclusive)
117 }
118
119 pub(crate) fn accepts_more(&self, current: usize) -> bool {
120 current < self.end_inclusive
121 }
122}
123
124impl std::ops::RangeBounds<usize> for ValueRange {
125 fn start_bound(&self) -> std::ops::Bound<&usize> {
126 std::ops::Bound::Included(&self.start_inclusive)
127 }
128
129 fn end_bound(&self) -> std::ops::Bound<&usize> {
130 std::ops::Bound::Included(&self.end_inclusive)
131 }
132}
133
134impl Default for ValueRange {
135 fn default() -> Self {
136 Self::SINGLE
137 }
138}
139
140impl From<usize> for ValueRange {
141 fn from(fixed: usize) -> Self {
142 (fixed..=fixed).into()
143 }
144}
145
146impl From<std::ops::Range<usize>> for ValueRange {
147 fn from(range: std::ops::Range<usize>) -> Self {
148 let start_inclusive = range.start;
149 let end_inclusive = range.end.saturating_sub(1);
150 Self::raw(start_inclusive, end_inclusive)
151 }
152}
153
154impl From<std::ops::RangeFull> for ValueRange {
155 fn from(_: std::ops::RangeFull) -> Self {
156 Self::FULL
157 }
158}
159
160impl From<std::ops::RangeFrom<usize>> for ValueRange {
161 fn from(range: std::ops::RangeFrom<usize>) -> Self {
162 let start_inclusive = range.start;
163 let end_inclusive = usize::MAX;
164 Self::raw(start_inclusive, end_inclusive)
165 }
166}
167
168impl From<std::ops::RangeTo<usize>> for ValueRange {
169 fn from(range: std::ops::RangeTo<usize>) -> Self {
170 let start_inclusive = 0;
171 let end_inclusive = range.end.saturating_sub(1);
172 Self::raw(start_inclusive, end_inclusive)
173 }
174}
175
176impl From<std::ops::RangeInclusive<usize>> for ValueRange {
177 fn from(range: std::ops::RangeInclusive<usize>) -> Self {
178 let start_inclusive = *range.start();
179 let end_inclusive = *range.end();
180 Self::raw(start_inclusive, end_inclusive)
181 }
182}
183
184impl From<std::ops::RangeToInclusive<usize>> for ValueRange {
185 fn from(range: std::ops::RangeToInclusive<usize>) -> Self {
186 let start_inclusive = 0;
187 let end_inclusive = range.end;
188 Self::raw(start_inclusive, end_inclusive)
189 }
190}
191
192impl std::fmt::Display for ValueRange {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 ok!(self.start_inclusive.fmt(f));
195 if self.is_fixed() {
196 } else if self.end_inclusive == usize::MAX {
197 ok!("..".fmt(f));
198 } else {
199 ok!("..=".fmt(f));
200 ok!(self.end_inclusive.fmt(f));
201 }
202 Ok(())
203 }
204}
205
206impl std::fmt::Debug for ValueRange {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 write!(f, "{self}")
209 }
210}
211
212#[cfg(test)]
213mod test {
214 use super::*;
215
216 use std::ops::RangeBounds;
217
218 #[test]
219 fn from_fixed() {
220 let range: ValueRange = 5.into();
221 assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
222 assert_eq!(range.end_bound(), std::ops::Bound::Included(&5));
223 assert!(range.is_fixed());
224 assert!(range.is_multiple());
225 assert_eq!(range.num_values(), Some(5));
226 assert!(range.takes_values());
227 }
228
229 #[test]
230 fn from_fixed_empty() {
231 let range: ValueRange = 0.into();
232 assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
233 assert_eq!(range.end_bound(), std::ops::Bound::Included(&0));
234 assert!(range.is_fixed());
235 assert!(!range.is_multiple());
236 assert_eq!(range.num_values(), Some(0));
237 assert!(!range.takes_values());
238 }
239
240 #[test]
241 fn from_range() {
242 let range: ValueRange = (5..10).into();
243 assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
244 assert_eq!(range.end_bound(), std::ops::Bound::Included(&9));
245 assert!(!range.is_fixed());
246 assert!(range.is_multiple());
247 assert_eq!(range.num_values(), None);
248 assert!(range.takes_values());
249 }
250
251 #[test]
252 fn from_range_inclusive() {
253 let range: ValueRange = (5..=10).into();
254 assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
255 assert_eq!(range.end_bound(), std::ops::Bound::Included(&10));
256 assert!(!range.is_fixed());
257 assert!(range.is_multiple());
258 assert_eq!(range.num_values(), None);
259 assert!(range.takes_values());
260 }
261
262 #[test]
263 fn from_range_full() {
264 let range: ValueRange = (..).into();
265 assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
266 assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX));
267 assert!(!range.is_fixed());
268 assert!(range.is_multiple());
269 assert_eq!(range.num_values(), None);
270 assert!(range.takes_values());
271 }
272
273 #[test]
274 fn from_range_from() {
275 let range: ValueRange = (5..).into();
276 assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
277 assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX));
278 assert!(!range.is_fixed());
279 assert!(range.is_multiple());
280 assert_eq!(range.num_values(), None);
281 assert!(range.takes_values());
282 }
283
284 #[test]
285 fn from_range_to() {
286 let range: ValueRange = (..10).into();
287 assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
288 assert_eq!(range.end_bound(), std::ops::Bound::Included(&9));
289 assert!(!range.is_fixed());
290 assert!(range.is_multiple());
291 assert_eq!(range.num_values(), None);
292 assert!(range.takes_values());
293 }
294
295 #[test]
296 fn from_range_to_inclusive() {
297 let range: ValueRange = (..=10).into();
298 assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
299 assert_eq!(range.end_bound(), std::ops::Bound::Included(&10));
300 assert!(!range.is_fixed());
301 assert!(range.is_multiple());
302 assert_eq!(range.num_values(), None);
303 assert!(range.takes_values());
304 }
305}