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