version_number/parsers/modular/parser.rs
1use super::component::{is_done, parse_component, parse_dot, peek_is_dot};
2use super::error::ModularParserError;
3use crate::{BaseVersion, FullVersion, Version};
4use std::iter::Peekable;
5use std::slice::Iter;
6
7// States
8
9/// A parser state of a _modular parser_.
10///
11/// This is the initial state.
12#[derive(Debug)]
13pub struct Unparsed;
14
15/// A parser state of a _modular parser_.
16///
17/// When the parser has reached this state, a two component [`BaseVersion`]
18/// has been parsed, although no end-of-input check has taken place.
19///
20/// For example, given the hypothetical input `a.bc`, we are only aware that
21/// `a.b` is a valid two component version number, while the tokens `c` may be
22/// anything. In practice, the `c` tokens cannot be the complete set of available
23/// tokens because the component parsing implementation has to distinguish
24/// between the tokens in the component (i.e. the number) and those outside of
25/// it. This though, is only an implementation detail, and not a concern of the
26/// API.
27#[derive(Debug)]
28pub struct ParsedBase {
29 version: BaseVersion,
30}
31
32/// A parser state of a _modular parser_.
33///
34/// When the parser has reached this state, a three component [`FullVersion`]
35/// has been parsed, although no end-of-input check has taken place.
36///
37/// For example, given the hypothetical input `a.b.cd`, we are only aware that
38/// `a.b.c` is a valid three component version number, while the tokens `d` may
39/// be anything. In practice, the `d` tokens cannot be the complete set of
40/// available tokens because the component parsing implementation has to
41/// distinguish between the tokens in the component (i.e. the number)
42/// and those outside of it. This though, is only an implementation detail,
43/// and not a concern of the API.
44#[derive(Debug)]
45pub struct ParsedFull {
46 version: FullVersion,
47}
48
49/// A trait to restrict the state of the [`Parser`] to valid state instances.
50///
51/// Since this trait is public it can technically be implemented outside of this
52/// crate. Usually, this is not what you want though, although it may be useful
53/// for testing, which is why it's not sealed.
54pub trait ParsedState {}
55impl ParsedState for Unparsed {}
56impl ParsedState for ParsedBase {}
57impl ParsedState for ParsedFull {}
58
59// Parser
60
61/// A parser which may be used to parse a [`Version`] or its discriminants ([`BaseVersion`] and
62/// [`FullVersion`]), incrementally.
63#[derive(Debug)]
64pub struct Parser<'p, S: ParsedState> {
65 state: S,
66 iter: Peekable<Iter<'p, u8>>,
67}
68
69impl<'p> Parser<'p, Unparsed> {
70 /// Construct a parser from a byte slice.
71 ///
72 /// # Example
73 ///
74 /// ```
75 /// use version_number::parsers::modular::Parser;
76 /// let parser = Parser::from_slice("1.0.0".as_bytes());
77 /// ```
78 pub fn from_slice(bytes: &'p [u8]) -> Parser<'p, Unparsed> {
79 let iter = bytes.iter();
80
81 Parser {
82 state: Unparsed,
83 iter: iter.peekable(),
84 }
85 }
86}
87
88impl<'p> Parser<'p, Unparsed> {
89 /// Parse the base of a [`Version`]. The `base` are the `major` and `minor` components
90 /// of a version. An example of a `base` version which will parse, would be `1.2`.
91 ///
92 /// This method returns another [`Parser`] instance. To get the parsed version
93 /// after parsing the `base`, you may use [`Parser::finish`].
94 ///
95 /// In case you want to either parse a two or three component version, and you
96 /// don't care which one you have, you should use [`Parser::parse`] instead.
97 ///
98 /// # Example
99 ///
100 /// ```
101 /// use version_number::BaseVersion;
102 /// use version_number::parsers::modular::{Parser};
103 ///
104 /// let parser = Parser::from_slice("1.2".as_bytes());
105 ///
106 /// let base = parser.parse_base().unwrap();
107 ///
108 /// assert_eq!(base.inner_version(), &BaseVersion::new(1, 2));
109 /// ```
110 pub fn parse_base(self) -> Result<Parser<'p, ParsedBase>, ModularParserError> {
111 let Self { mut iter, .. } = self;
112
113 let major = parse_component(iter.by_ref())?;
114 parse_dot(iter.by_ref())?;
115 let minor = parse_component(iter.by_ref())?;
116
117 let version = BaseVersion::new(major, minor);
118
119 Ok(Parser {
120 state: ParsedBase { version },
121 iter,
122 })
123 }
124
125 /// Parse a full, three component major, minor, patch [`Version`]. A two
126 /// component input, consisting of only the major and minor components, will be rejected.
127 ///
128 /// # Example
129 ///
130 /// ```
131 /// use version_number::{BaseVersion, FullVersion};
132 /// use version_number::parsers::modular::{Parser};
133 /// let parser = Parser::from_slice("1.2.3".as_bytes());
134 ///
135 /// let base = parser.parse_full().unwrap();
136 ///
137 /// assert_eq!(base.inner_version(), &FullVersion::new(1, 2, 3));
138 /// ```
139 pub fn parse_full(self) -> Result<Parser<'p, ParsedFull>, ModularParserError> {
140 let parser = self.parse_base()?;
141 parser.parse_patch()
142 }
143
144 /// Parse a `base`, two component `major.minor` [`Version`], or a `full`, three component `major.minor.patch`,
145 /// depending on the input.
146 ///
147 /// # Example 1
148 ///
149 /// ```
150 /// use version_number::{BaseVersion, FullVersion, Version};
151 /// use version_number::parsers::modular::Parser;
152 ///
153 /// let parser = Parser::from_slice("1.2".as_bytes());
154 ///
155 /// let version = parser.parse();
156 ///
157 /// assert_eq!(version.unwrap(), Version::Base(BaseVersion::new(1, 2)));
158 /// ```
159 ///
160 /// # Example 2
161 ///
162 /// ```
163 /// use version_number::{FullVersion, Version};
164 /// use version_number::parsers::modular::Parser;
165 ///
166 /// let parser = Parser::from_slice("1.2.3".as_bytes());
167 ///
168 /// let version = parser.parse();
169 ///
170 /// assert_eq!(version.unwrap(), Version::Full(FullVersion::new(1, 2, 3)));
171 /// ```
172 ///
173 /// # Example 3
174 ///
175 /// ```
176 /// use version_number::{FullVersion, Version};
177 /// use version_number::parsers::modular::Parser;
178 ///
179 /// let parser = Parser::from_slice("1.2.".as_bytes());
180 ///
181 /// let version = parser.parse();
182 ///
183 /// assert!(version.is_err());
184 /// ```
185 pub fn parse(self) -> Result<Version, ModularParserError> {
186 let mut parser = self.parse_base()?;
187
188 if peek_is_dot(parser.iter.by_ref()) {
189 parser.parse_patch()?.finish()
190 } else {
191 parser.finish()
192 }
193 }
194}
195
196impl<'p> Parser<'p, ParsedBase> {
197 /// Parse the patch component, to produce a [`FullVersion`].
198 ///
199 /// # Example
200 ///
201 /// ```
202 /// use version_number::{FullVersion};
203 /// use version_number::parsers::modular::Parser;
204 ///
205 /// let input = "1.2.3";
206 ///
207 /// let parser = Parser::from_slice(input.as_bytes());
208 /// let full = parser
209 /// .parse_base()
210 /// .unwrap()
211 /// .parse_patch()
212 /// .unwrap();
213 ///
214 /// assert_eq!(full.inner_version(), &FullVersion::new(1, 2, 3));
215 /// ```
216 pub fn parse_patch(self) -> Result<Parser<'p, ParsedFull>, ModularParserError> {
217 let Self {
218 mut iter,
219 state: ParsedBase {
220 version: BaseVersion { major, minor },
221 },
222 } = self;
223
224 parse_dot(iter.by_ref())?;
225 let patch = parse_component(iter.by_ref())?;
226
227 let version = FullVersion::new(major, minor, patch);
228
229 Ok(Parser {
230 state: ParsedFull { version },
231 iter,
232 })
233 }
234
235 /// Parses a `patch` component if it exists and returns a [`Version::Full`],
236 /// or if the input does not have a third component, returns the two component [`Version::Base`]
237 /// variant instead.
238 ///
239 /// Prefer [`Parser::parse`] over this method when possible, as this method clones the underlying
240 /// iterator to determine whether we do have additional content.
241 pub fn parse_patch_or_finish(self) -> Result<Version, ModularParserError> {
242 if peek_is_dot(self.iter.clone().by_ref()) {
243 self.finish()
244 } else {
245 self.parse_patch()?.finish()
246 }
247 }
248
249 /// Checks that there is no remaining input, and returns a [`Version`], which
250 /// wraps the parsed base version.
251 ///
252 /// When there is remaining input, this method will return a [`ModularParserError::ExpectedEOI`]
253 /// instead.
254 pub fn finish(self) -> Result<Version, ModularParserError> {
255 self.finish_base_version().map(Version::Base)
256 }
257
258 /// Checks that there is no remaining input, and returns a [`BaseVersion`].
259 ///
260 /// When there is remaining input, this method will return a [`ModularParserError::ExpectedEOI`]
261 /// instead.
262 pub fn finish_base_version(self) -> Result<BaseVersion, ModularParserError> {
263 let Self { mut iter, state } = self;
264
265 is_done(iter.by_ref())?;
266
267 Ok(state.version)
268 }
269
270 /// Returns the so far successfully parsed version state.
271 ///
272 /// **NB:** Unless the end of input has been reached, this version may not be valid.
273 pub fn inner_version(&self) -> &BaseVersion {
274 &self.state.version
275 }
276}
277
278impl<'p> Parser<'p, ParsedFull> {
279 /// Checks that there is no remaining input, and returns a [`Version`], which
280 /// wraps the parsed base version.
281 ///
282 /// When there is remaining input, this method will return a [`ModularParserError::ExpectedEOI`]
283 pub fn finish(self) -> Result<Version, ModularParserError> {
284 let Self { mut iter, state } = self;
285
286 is_done(iter.by_ref())?;
287
288 Ok(Version::Full(state.version))
289 }
290
291 /// Checks that there is no remaining input, and returns a [`FullVersion`].
292 ///
293 /// When there is remaining input, this method will return a [`ModularParserError::ExpectedEOI`]
294 /// instead.
295 pub fn finish_full_version(self) -> Result<FullVersion, ModularParserError> {
296 let Self { mut iter, state } = self;
297
298 is_done(iter.by_ref())?;
299
300 Ok(state.version)
301 }
302
303 /// Returns the so far successfully parsed version.
304 ///
305 /// **NB:** Unless the end of input has been reached, this version may not be valid.
306 pub fn inner_version(&self) -> &FullVersion {
307 &self.state.version
308 }
309}
310
311#[cfg(test)]
312mod tests_leading_zeros {
313 use super::*;
314 use crate::parsers::modular::NumberError;
315 use crate::BaseVersion;
316 use yare::parameterized;
317
318 #[test]
319 fn test() {}
320
321 #[parameterized(
322 zeroes = { "0.0", 0, 0 },
323 one = { "1.0", 1, 0 },
324 ones = { "1.1", 1, 1 },
325 )]
326 fn accepted(input: &str, major: u64, minor: u64) {
327 let input = input.as_bytes();
328 let parsed = Parser::from_slice(input)
329 .parse_base()
330 .and_then(|parser| parser.finish_base_version())
331 .unwrap();
332
333 assert_eq!(BaseVersion::new(major, minor), parsed);
334 }
335
336 #[parameterized(
337 no_leading_zero_component_0 = { "00.0", ModularParserError::NumberError(NumberError::LeadingZero) },
338 no_leading_zero_component_1 = { "01.0", ModularParserError::NumberError(NumberError::LeadingZero) },
339 no_leading_zero_component_2 = { "1.01", ModularParserError::NumberError(NumberError::LeadingZero) },
340 )]
341 fn rejected(input: &str, expected_err: ModularParserError) {
342 let input = input.as_bytes();
343 let err = Parser::from_slice(input)
344 .parse_base()
345 .and_then(|parser| parser.finish_base_version())
346 .unwrap_err();
347
348 assert_eq!(err, expected_err);
349 }
350}
351
352#[cfg(test)]
353mod tests_parser_base {
354 use super::*;
355 use crate::parsers::modular::NumberError;
356 use crate::BaseVersion;
357 use yare::parameterized;
358
359 #[test]
360 fn test() {}
361
362 #[parameterized(
363 zeroes = { "0.0", 0, 0 },
364 one = { "1.0", 1, 0 },
365 ones = { "1.1", 1, 1 },
366 )]
367 fn accepted(input: &str, major: u64, minor: u64) {
368 let parser = Parser::from_slice(input.as_bytes());
369
370 let base = parser.parse_base().unwrap();
371 let version = base.inner_version();
372
373 assert_eq!(&BaseVersion::new(major, minor), version);
374
375 let version = base.finish().unwrap();
376 assert_eq!(Version::new_base_version(major, minor), version);
377 }
378
379 #[test]
380 fn rejected_on_no_input() {
381 let input = "";
382 let parser = Parser::from_slice(input.as_bytes());
383 let err = parser.parse_base().unwrap_err();
384
385 assert_eq!(err, ModularParserError::ExpectedNumericToken { got: None });
386 }
387
388 #[test]
389 fn rejected_on_no_input2() {
390 let input = "1.";
391 let parser = Parser::from_slice(input.as_bytes());
392 let err = parser.parse_base().unwrap_err();
393
394 assert_eq!(err, ModularParserError::ExpectedNumericToken { got: None });
395 }
396
397 #[test]
398 fn rejected_on_overflow() {
399 // u64::MAX is accepted
400 let input = format!("{}.{}5", u64::MAX, 1844674407370955161_u64);
401 let parser = Parser::from_slice(input.as_bytes());
402 assert!(parser.parse_base().is_ok());
403
404 // but u64::MAX+1 overflows
405 let input = format!("{}6.0", 1844674407370955161_u64);
406 let parser = Parser::from_slice(input.as_bytes());
407 let err = parser.parse_base().unwrap_err();
408
409 assert_eq!(err, ModularParserError::NumberError(NumberError::Overflow));
410 }
411
412 #[test]
413 fn rejected_on_separator_expected() {
414 let input = "1";
415 let parser = Parser::from_slice(input.as_bytes());
416 let err = parser.parse_base().unwrap_err();
417
418 assert_eq!(err, ModularParserError::ExpectedSeparator { got: None });
419 }
420
421 #[test]
422 fn rejected_on_eoi_expected() {
423 let input = "1.0.0";
424 let parser = Parser::from_slice(input.as_bytes());
425 let err = parser.parse_base().unwrap().finish().unwrap_err();
426
427 assert_eq!(err, ModularParserError::ExpectedEndOfInput { got: b'.' });
428 }
429
430 #[test]
431 fn rejected_on_leading_zero_not_allowed() {
432 let input = "1.01";
433 let parser = Parser::from_slice(input.as_bytes());
434 let err = parser.parse_base().unwrap_err();
435
436 assert_eq!(
437 err,
438 ModularParserError::NumberError(NumberError::LeadingZero)
439 );
440 }
441
442 #[parameterized(
443 in_first_component_1 = { "01.9" },
444 in_first_component_2 = { "00.9" },
445 in_second_component_1 = { "9.01" },
446 in_second_component_2 = { "9.00" },
447 )]
448 fn rejected_on_leading_zero_not_allowed(input: &str) {
449 let parser = Parser::from_slice(input.as_bytes());
450 let err = parser.parse_base().unwrap_err();
451
452 assert_eq!(
453 err,
454 ModularParserError::NumberError(NumberError::LeadingZero)
455 );
456 }
457}