clap_builder/builder/
range.rs

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    #[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
117impl 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
127impl Default for ValueRange {
128    fn default() -> Self {
129        Self::SINGLE
130    }
131}
132
133impl From<usize> for ValueRange {
134    fn from(fixed: usize) -> Self {
135        (fixed..=fixed).into()
136    }
137}
138
139impl From<std::ops::Range<usize>> for ValueRange {
140    fn from(range: std::ops::Range<usize>) -> Self {
141        let start_inclusive = range.start;
142        let end_inclusive = range.end.saturating_sub(1);
143        Self::raw(start_inclusive, end_inclusive)
144    }
145}
146
147impl From<std::ops::RangeFull> for ValueRange {
148    fn from(_: std::ops::RangeFull) -> Self {
149        Self::FULL
150    }
151}
152
153impl From<std::ops::RangeFrom<usize>> for ValueRange {
154    fn from(range: std::ops::RangeFrom<usize>) -> Self {
155        let start_inclusive = range.start;
156        let end_inclusive = usize::MAX;
157        Self::raw(start_inclusive, end_inclusive)
158    }
159}
160
161impl From<std::ops::RangeTo<usize>> for ValueRange {
162    fn from(range: std::ops::RangeTo<usize>) -> Self {
163        let start_inclusive = 0;
164        let end_inclusive = range.end.saturating_sub(1);
165        Self::raw(start_inclusive, end_inclusive)
166    }
167}
168
169impl From<std::ops::RangeInclusive<usize>> for ValueRange {
170    fn from(range: std::ops::RangeInclusive<usize>) -> Self {
171        let start_inclusive = *range.start();
172        let end_inclusive = *range.end();
173        Self::raw(start_inclusive, end_inclusive)
174    }
175}
176
177impl From<std::ops::RangeToInclusive<usize>> for ValueRange {
178    fn from(range: std::ops::RangeToInclusive<usize>) -> Self {
179        let start_inclusive = 0;
180        let end_inclusive = range.end;
181        Self::raw(start_inclusive, end_inclusive)
182    }
183}
184
185impl 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
199impl 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)]
206mod 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}