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    /// 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
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 = range.start;
131        let end_inclusive = 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 = 0;
139        let end_inclusive = 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 = range.start;
147        let end_inclusive = 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 = 0;
155        let end_inclusive = 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 = *range.start();
163        let end_inclusive = *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 = 0;
171        let end_inclusive = 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}