1 | /// Values per occurrence for an argument |
2 | #[derive (Copy, Clone, PartialEq, Eq, Hash)] |
3 | pub struct ValueRange { |
4 | start_inclusive: usize, |
5 | end_inclusive: usize, |
6 | } |
7 | |
8 | impl ValueRange { |
9 | /// Nor argument values, or a flag |
10 | pub const EMPTY: Self = Self { |
11 | start_inclusive: 0, |
12 | end_inclusive: 0, |
13 | }; |
14 | |
15 | /// A single argument value, the most common case for options |
16 | 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 | /// Create a range |
33 | /// |
34 | /// # Panics |
35 | /// |
36 | /// If the end is less than the start (debug builds) |
37 | /// |
38 | /// # Examples |
39 | /// |
40 | /// ```rust |
41 | /// # use clap_builder as clap; |
42 | /// # use clap::builder::ValueRange; |
43 | /// let range = ValueRange::new(5); |
44 | /// let range = ValueRange::new(5..10); |
45 | /// let range = ValueRange::new(5..=10); |
46 | /// let range = ValueRange::new(5..); |
47 | /// let range = ValueRange::new(..10); |
48 | /// let range = ValueRange::new(..=10); |
49 | /// ``` |
50 | /// |
51 | /// While this will panic: |
52 | /// ```should_panic |
53 | /// # use clap_builder as clap; |
54 | /// # use clap::builder::ValueRange; |
55 | /// let range = ValueRange::new(10..5); // Panics! |
56 | /// ``` |
57 | pub fn new(range: impl Into<Self>) -> Self { |
58 | range.into() |
59 | } |
60 | |
61 | pub(crate) fn raw(start_inclusive: usize, end_inclusive: usize) -> Self { |
62 | debug_assert!(start_inclusive <= end_inclusive); |
63 | Self { |
64 | start_inclusive, |
65 | end_inclusive, |
66 | } |
67 | } |
68 | |
69 | /// Fewest number of values the argument accepts |
70 | pub fn min_values(&self) -> usize { |
71 | self.start_inclusive |
72 | } |
73 | |
74 | /// Most number of values the argument accepts |
75 | pub fn max_values(&self) -> usize { |
76 | self.end_inclusive |
77 | } |
78 | |
79 | /// Report whether the argument takes any values (ie is a flag) |
80 | /// |
81 | /// # Examples |
82 | /// |
83 | /// ```rust |
84 | /// # use clap_builder as clap; |
85 | /// # use clap::builder::ValueRange; |
86 | /// let range = ValueRange::new(5); |
87 | /// assert!(range.takes_values()); |
88 | /// |
89 | /// let range = ValueRange::new(0); |
90 | /// assert!(!range.takes_values()); |
91 | /// ``` |
92 | pub fn takes_values(&self) -> bool { |
93 | self.end_inclusive != 0 |
94 | } |
95 | |
96 | pub(crate) fn is_unbounded(&self) -> bool { |
97 | self.end_inclusive == usize::MAX |
98 | } |
99 | |
100 | pub(crate) fn is_fixed(&self) -> bool { |
101 | self.start_inclusive == self.end_inclusive |
102 | } |
103 | |
104 | pub(crate) fn is_multiple(&self) -> bool { |
105 | self.start_inclusive != self.end_inclusive || 1 < self.start_inclusive |
106 | } |
107 | |
108 | pub(crate) fn num_values(&self) -> Option<usize> { |
109 | self.is_fixed().then_some(self.start_inclusive) |
110 | } |
111 | |
112 | pub(crate) fn accepts_more(&self, current: usize) -> bool { |
113 | current < self.end_inclusive |
114 | } |
115 | } |
116 | |
117 | impl std::ops::RangeBounds<usize> for ValueRange { |
118 | fn start_bound(&self) -> std::ops::Bound<&usize> { |
119 | std::ops::Bound::Included(&self.start_inclusive) |
120 | } |
121 | |
122 | fn end_bound(&self) -> std::ops::Bound<&usize> { |
123 | std::ops::Bound::Included(&self.end_inclusive) |
124 | } |
125 | } |
126 | |
127 | impl Default for ValueRange { |
128 | fn default() -> Self { |
129 | Self::SINGLE |
130 | } |
131 | } |
132 | |
133 | impl From<usize> for ValueRange { |
134 | fn from(fixed: usize) -> Self { |
135 | (fixed..=fixed).into() |
136 | } |
137 | } |
138 | |
139 | impl From<std::ops::Range<usize>> for ValueRange { |
140 | fn from(range: std::ops::Range<usize>) -> Self { |
141 | let start_inclusive: usize = range.start; |
142 | let end_inclusive: usize = range.end.saturating_sub(1); |
143 | Self::raw(start_inclusive, end_inclusive) |
144 | } |
145 | } |
146 | |
147 | impl From<std::ops::RangeFull> for ValueRange { |
148 | fn from(_: std::ops::RangeFull) -> Self { |
149 | Self::FULL |
150 | } |
151 | } |
152 | |
153 | impl From<std::ops::RangeFrom<usize>> for ValueRange { |
154 | fn from(range: std::ops::RangeFrom<usize>) -> Self { |
155 | let start_inclusive: usize = range.start; |
156 | let end_inclusive: usize = usize::MAX; |
157 | Self::raw(start_inclusive, end_inclusive) |
158 | } |
159 | } |
160 | |
161 | impl From<std::ops::RangeTo<usize>> for ValueRange { |
162 | fn from(range: std::ops::RangeTo<usize>) -> Self { |
163 | let start_inclusive: usize = 0; |
164 | let end_inclusive: usize = range.end.saturating_sub(1); |
165 | Self::raw(start_inclusive, end_inclusive) |
166 | } |
167 | } |
168 | |
169 | impl From<std::ops::RangeInclusive<usize>> for ValueRange { |
170 | fn from(range: std::ops::RangeInclusive<usize>) -> Self { |
171 | let start_inclusive: usize = *range.start(); |
172 | let end_inclusive: usize = *range.end(); |
173 | Self::raw(start_inclusive, end_inclusive) |
174 | } |
175 | } |
176 | |
177 | impl From<std::ops::RangeToInclusive<usize>> for ValueRange { |
178 | fn from(range: std::ops::RangeToInclusive<usize>) -> Self { |
179 | let start_inclusive: usize = 0; |
180 | let end_inclusive: usize = range.end; |
181 | Self::raw(start_inclusive, end_inclusive) |
182 | } |
183 | } |
184 | |
185 | impl std::fmt::Display for ValueRange { |
186 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
187 | ok!(self.start_inclusive.fmt(f)); |
188 | if self.is_fixed() { |
189 | } else if self.end_inclusive == usize::MAX { |
190 | ok!(".." .fmt(f)); |
191 | } else { |
192 | ok!("..=" .fmt(f)); |
193 | ok!(self.end_inclusive.fmt(f)); |
194 | } |
195 | Ok(()) |
196 | } |
197 | } |
198 | |
199 | impl std::fmt::Debug for ValueRange { |
200 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
201 | write!(f, " {self}" ) |
202 | } |
203 | } |
204 | |
205 | #[cfg (test)] |
206 | mod test { |
207 | use super::*; |
208 | |
209 | use std::ops::RangeBounds; |
210 | |
211 | #[test ] |
212 | fn from_fixed() { |
213 | let range: ValueRange = 5.into(); |
214 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&5)); |
215 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&5)); |
216 | assert!(range.is_fixed()); |
217 | assert!(range.is_multiple()); |
218 | assert_eq!(range.num_values(), Some(5)); |
219 | assert!(range.takes_values()); |
220 | } |
221 | |
222 | #[test ] |
223 | fn from_fixed_empty() { |
224 | let range: ValueRange = 0.into(); |
225 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&0)); |
226 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&0)); |
227 | assert!(range.is_fixed()); |
228 | assert!(!range.is_multiple()); |
229 | assert_eq!(range.num_values(), Some(0)); |
230 | assert!(!range.takes_values()); |
231 | } |
232 | |
233 | #[test ] |
234 | fn from_range() { |
235 | let range: ValueRange = (5..10).into(); |
236 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&5)); |
237 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&9)); |
238 | assert!(!range.is_fixed()); |
239 | assert!(range.is_multiple()); |
240 | assert_eq!(range.num_values(), None); |
241 | assert!(range.takes_values()); |
242 | } |
243 | |
244 | #[test ] |
245 | fn from_range_inclusive() { |
246 | let range: ValueRange = (5..=10).into(); |
247 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&5)); |
248 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&10)); |
249 | assert!(!range.is_fixed()); |
250 | assert!(range.is_multiple()); |
251 | assert_eq!(range.num_values(), None); |
252 | assert!(range.takes_values()); |
253 | } |
254 | |
255 | #[test ] |
256 | fn from_range_full() { |
257 | let range: ValueRange = (..).into(); |
258 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&0)); |
259 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX)); |
260 | assert!(!range.is_fixed()); |
261 | assert!(range.is_multiple()); |
262 | assert_eq!(range.num_values(), None); |
263 | assert!(range.takes_values()); |
264 | } |
265 | |
266 | #[test ] |
267 | fn from_range_from() { |
268 | let range: ValueRange = (5..).into(); |
269 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&5)); |
270 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX)); |
271 | assert!(!range.is_fixed()); |
272 | assert!(range.is_multiple()); |
273 | assert_eq!(range.num_values(), None); |
274 | assert!(range.takes_values()); |
275 | } |
276 | |
277 | #[test ] |
278 | fn from_range_to() { |
279 | let range: ValueRange = (..10).into(); |
280 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&0)); |
281 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&9)); |
282 | assert!(!range.is_fixed()); |
283 | assert!(range.is_multiple()); |
284 | assert_eq!(range.num_values(), None); |
285 | assert!(range.takes_values()); |
286 | } |
287 | |
288 | #[test ] |
289 | fn from_range_to_inclusive() { |
290 | let range: ValueRange = (..=10).into(); |
291 | assert_eq!(range.start_bound(), std::ops::Bound::Included(&0)); |
292 | assert_eq!(range.end_bound(), std::ops::Bound::Included(&10)); |
293 | assert!(!range.is_fixed()); |
294 | assert!(range.is_multiple()); |
295 | assert_eq!(range.num_values(), None); |
296 | assert!(range.takes_values()); |
297 | } |
298 | } |
299 | |