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 |