use super::component::{is_done, parse_component, parse_dot, peek_is_dot};
use super::error::ModularParserError;
use crate::{BaseVersion, FullVersion, Version};
use std::iter::Peekable;
use std::slice::Iter;
#[derive(Debug)]
pub struct Unparsed;
#[derive(Debug)]
pub struct ParsedBase {
version: BaseVersion,
}
#[derive(Debug)]
pub struct ParsedFull {
version: FullVersion,
}
pub trait ParsedState {}
impl ParsedState for Unparsed {}
impl ParsedState for ParsedBase {}
impl ParsedState for ParsedFull {}
#[derive(Debug)]
pub struct Parser<'p, S: ParsedState> {
state: S,
iter: Peekable<Iter<'p, u8>>,
}
impl<'p> Parser<'p, Unparsed> {
pub fn from_slice(bytes: &'p [u8]) -> Parser<'p, Unparsed> {
let iter = bytes.iter();
Parser {
state: Unparsed,
iter: iter.peekable(),
}
}
}
impl<'p> Parser<'p, Unparsed> {
pub fn parse_base(self) -> Result<Parser<'p, ParsedBase>, ModularParserError> {
let Self { mut iter, .. } = self;
let major = parse_component(iter.by_ref())?;
parse_dot(iter.by_ref())?;
let minor = parse_component(iter.by_ref())?;
let version = BaseVersion::new(major, minor);
Ok(Parser {
state: ParsedBase { version },
iter,
})
}
pub fn parse_full(self) -> Result<Parser<'p, ParsedFull>, ModularParserError> {
let parser = self.parse_base()?;
parser.parse_patch()
}
pub fn parse(self) -> Result<Version, ModularParserError> {
let mut parser = self.parse_base()?;
if peek_is_dot(parser.iter.by_ref()) {
parser.parse_patch()?.finish()
} else {
parser.finish()
}
}
}
impl<'p> Parser<'p, ParsedBase> {
pub fn parse_patch(self) -> Result<Parser<'p, ParsedFull>, ModularParserError> {
let Self {
mut iter,
state: ParsedBase {
version: BaseVersion { major, minor },
},
} = self;
parse_dot(iter.by_ref())?;
let patch = parse_component(iter.by_ref())?;
let version = FullVersion::new(major, minor, patch);
Ok(Parser {
state: ParsedFull { version },
iter,
})
}
pub fn parse_patch_or_finish(self) -> Result<Version, ModularParserError> {
if peek_is_dot(self.iter.clone().by_ref()) {
self.finish()
} else {
self.parse_patch()?.finish()
}
}
pub fn finish(self) -> Result<Version, ModularParserError> {
self.finish_base_version().map(Version::Base)
}
pub fn finish_base_version(self) -> Result<BaseVersion, ModularParserError> {
let Self { mut iter, state } = self;
is_done(iter.by_ref())?;
Ok(state.version)
}
pub fn inner_version(&self) -> &BaseVersion {
&self.state.version
}
}
impl<'p> Parser<'p, ParsedFull> {
pub fn finish(self) -> Result<Version, ModularParserError> {
let Self { mut iter, state } = self;
is_done(iter.by_ref())?;
Ok(Version::Full(state.version))
}
pub fn finish_full_version(self) -> Result<FullVersion, ModularParserError> {
let Self { mut iter, state } = self;
is_done(iter.by_ref())?;
Ok(state.version)
}
pub fn inner_version(&self) -> &FullVersion {
&self.state.version
}
}
#[cfg(test)]
mod tests_leading_zeros {
use super::*;
use crate::BaseVersion;
use crate::parsers::modular::NumberError;
use yare::parameterized;
#[test]
fn test() {}
#[parameterized(
zeroes = { "0.0", 0, 0 },
one = { "1.0", 1, 0 },
ones = { "1.1", 1, 1 },
)]
fn accepted(input: &str, major: u64, minor: u64) {
let input = input.as_bytes();
let parsed = Parser::from_slice(input)
.parse_base()
.and_then(|parser| parser.finish_base_version())
.unwrap();
assert_eq!(BaseVersion::new(major, minor), parsed);
}
#[parameterized(
no_leading_zero_component_0 = { "00.0", ModularParserError::NumberError(NumberError::LeadingZero) },
no_leading_zero_component_1 = { "01.0", ModularParserError::NumberError(NumberError::LeadingZero) },
no_leading_zero_component_2 = { "1.01", ModularParserError::NumberError(NumberError::LeadingZero) },
)]
fn rejected(input: &str, expected_err: ModularParserError) {
let input = input.as_bytes();
let err = Parser::from_slice(input)
.parse_base()
.and_then(|parser| parser.finish_base_version())
.unwrap_err();
assert_eq!(err, expected_err);
}
}
#[cfg(test)]
mod tests_parser_base {
use super::*;
use crate::BaseVersion;
use crate::parsers::modular::NumberError;
use yare::parameterized;
#[test]
fn test() {}
#[parameterized(
zeroes = { "0.0", 0, 0 },
one = { "1.0", 1, 0 },
ones = { "1.1", 1, 1 },
)]
fn accepted(input: &str, major: u64, minor: u64) {
let parser = Parser::from_slice(input.as_bytes());
let base = parser.parse_base().unwrap();
let version = base.inner_version();
assert_eq!(&BaseVersion::new(major, minor), version);
let version = base.finish().unwrap();
assert_eq!(Version::new_base_version(major, minor), version);
}
#[test]
fn rejected_on_no_input() {
let input = "";
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap_err();
assert_eq!(err, ModularParserError::ExpectedNumericToken { got: None });
}
#[test]
fn rejected_on_no_input2() {
let input = "1.";
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap_err();
assert_eq!(err, ModularParserError::ExpectedNumericToken { got: None });
}
#[test]
fn rejected_on_overflow() {
let input = format!("{}.{}5", u64::MAX, 1844674407370955161_u64);
let parser = Parser::from_slice(input.as_bytes());
assert!(parser.parse_base().is_ok());
let input = format!("{}6.0", 1844674407370955161_u64);
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap_err();
assert_eq!(err, ModularParserError::NumberError(NumberError::Overflow));
}
#[test]
fn rejected_on_separator_expected() {
let input = "1";
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap_err();
assert_eq!(err, ModularParserError::ExpectedSeparator { got: None });
}
#[test]
fn rejected_on_eoi_expected() {
let input = "1.0.0";
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap().finish().unwrap_err();
assert_eq!(err, ModularParserError::ExpectedEndOfInput { got: b'.' });
}
#[test]
fn rejected_on_leading_zero_not_allowed() {
let input = "1.01";
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap_err();
assert_eq!(
err,
ModularParserError::NumberError(NumberError::LeadingZero)
);
}
#[parameterized(
in_first_component_1 = { "01.9" },
in_first_component_2 = { "00.9" },
in_second_component_1 = { "9.01" },
in_second_component_2 = { "9.00" },
)]
fn rejected_on_leading_zero_not_allowed(input: &str) {
let parser = Parser::from_slice(input.as_bytes());
let err = parser.parse_base().unwrap_err();
assert_eq!(
err,
ModularParserError::NumberError(NumberError::LeadingZero)
);
}
}