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