1/// Values per occurrence for an argument
2#[derive(Copy, Clone, PartialEq, Eq, Hash)]
3pub struct ValueRange {
4 start_inclusive: usize,
5 end_inclusive: usize,
6}
7
8impl 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
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
106impl 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
116impl Default for ValueRange {
117 fn default() -> Self {
118 Self::SINGLE
119 }
120}
121
122impl From<usize> for ValueRange {
123 fn from(fixed: usize) -> Self {
124 (fixed..=fixed).into()
125 }
126}
127
128impl From<std::ops::Range<usize>> for ValueRange {
129 fn from(range: std::ops::Range<usize>) -> Self {
130 let start_inclusive: usize = range.start;
131 let end_inclusive: usize = range.end.saturating_sub(1);
132 Self::raw(start_inclusive, end_inclusive)
133 }
134}
135
136impl From<std::ops::RangeFull> for ValueRange {
137 fn from(_: std::ops::RangeFull) -> Self {
138 let start_inclusive: usize = 0;
139 let end_inclusive: usize = usize::MAX;
140 Self::raw(start_inclusive, end_inclusive)
141 }
142}
143
144impl From<std::ops::RangeFrom<usize>> for ValueRange {
145 fn from(range: std::ops::RangeFrom<usize>) -> Self {
146 let start_inclusive: usize = range.start;
147 let end_inclusive: usize = usize::MAX;
148 Self::raw(start_inclusive, end_inclusive)
149 }
150}
151
152impl From<std::ops::RangeTo<usize>> for ValueRange {
153 fn from(range: std::ops::RangeTo<usize>) -> Self {
154 let start_inclusive: usize = 0;
155 let end_inclusive: usize = range.end.saturating_sub(1);
156 Self::raw(start_inclusive, end_inclusive)
157 }
158}
159
160impl From<std::ops::RangeInclusive<usize>> for ValueRange {
161 fn from(range: std::ops::RangeInclusive<usize>) -> Self {
162 let start_inclusive: usize = *range.start();
163 let end_inclusive: usize = *range.end();
164 Self::raw(start_inclusive, end_inclusive)
165 }
166}
167
168impl From<std::ops::RangeToInclusive<usize>> for ValueRange {
169 fn from(range: std::ops::RangeToInclusive<usize>) -> Self {
170 let start_inclusive: usize = 0;
171 let end_inclusive: usize = range.end;
172 Self::raw(start_inclusive, end_inclusive)
173 }
174}
175
176impl 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
187impl 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)]
194mod 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