use std::fmt;
use std::mem;
use self::Error::*;
use super::lexer::{self, Lexer, Token};
use crate::version::{Identifier, Version};
use thiserror::Error;
type PubgrubRange = pubgrub::Range<Version>;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Error)]
pub enum Error {
UnexpectedEnd,
UnexpectedToken(String),
Lexer(lexer::Error),
MoreInput(String),
EmptyPredicate,
EmptyRange,
MinorVersionMissing(u32),
PatchVersionMissing(u32, u32),
}
impl From<lexer::Error> for Error {
fn from(value: lexer::Error) -> Self {
Error::Lexer(value)
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
UnexpectedEnd => write!(fmt, "expected more input"),
UnexpectedToken(ref token) => write!(fmt, "encountered unexpected token: {:?}", token),
Lexer(ref error) => write!(fmt, "lexer error: {:?}", error),
MoreInput(ref tokens) => write!(fmt, "expected end of input, but got: {:?}", tokens),
EmptyPredicate => write!(fmt, "encountered empty predicate"),
EmptyRange => write!(fmt, "encountered empty range"),
MinorVersionMissing(major) => {
write!(fmt, "missing minor and patch versions: {:?}", major)
}
PatchVersionMissing(major, minor) => {
write!(fmt, "missing patch version: {:?}.{:?}", major, minor)
}
}
}
}
impl From<Error> for String {
fn from(value: Error) -> Self {
value.to_string()
}
}
pub struct Parser<'input> {
lexer: Lexer<'input>,
c1: Option<Token<'input>>,
}
impl<'input> Parser<'input> {
pub fn new(input: &'input str) -> Result<Parser<'input>, Error> {
let mut lexer = Lexer::new(input);
let c1 = if let Some(c1) = lexer.next() {
Some(c1?)
} else {
None
};
Ok(Parser { lexer, c1 })
}
#[inline(always)]
fn pop(&mut self) -> Result<Token<'input>, Error> {
let c1 = if let Some(c1) = self.lexer.next() {
Some(c1?)
} else {
None
};
mem::replace(&mut self.c1, c1).ok_or(UnexpectedEnd)
}
#[inline(always)]
fn peek(&mut self) -> Option<&Token<'input>> {
self.c1.as_ref()
}
fn skip_whitespace(&mut self) -> Result<(), Error> {
match self.peek() {
Some(&Token::Whitespace(_, _)) => self.pop().map(|_| ()),
_ => Ok(()),
}
}
fn expect_whitespace(&mut self) -> Result<(), Error> {
match self.pop()? {
Token::Whitespace(_, _) => Ok(()),
token => Err(UnexpectedToken(token.to_string())),
}
}
pub fn numeric(&mut self) -> Result<u32, Error> {
match self.pop()? {
Token::Numeric(number) => Ok(number),
token => Err(UnexpectedToken(token.to_string())),
}
}
fn dot(&mut self) -> Result<(), Error> {
match self.pop()? {
Token::Dot => Ok(()),
token => Err(UnexpectedToken(token.to_string())),
}
}
fn dot_numeric(&mut self) -> Result<u32, Error> {
self.dot()?;
self.numeric()
}
pub fn identifier(&mut self) -> Result<Identifier, Error> {
let identifier = match self.pop()? {
Token::AlphaNumeric(identifier) => {
Identifier::AlphaNumeric(identifier.to_string())
}
Token::Numeric(n) => Identifier::Numeric(n),
tok => return Err(UnexpectedToken(tok.to_string())),
};
if let Some(&Token::Hyphen) = self.peek() {
self.pop()?;
Ok(identifier
.concat("-")
.concat(&self.identifier()?.to_string()))
} else {
Ok(identifier)
}
}
fn pre(&mut self) -> Result<Vec<Identifier>, Error> {
match self.peek() {
Some(&Token::Hyphen) => {}
_ => return Ok(vec![]),
}
self.pop()?;
self.parts()
}
fn parts(&mut self) -> Result<Vec<Identifier>, Error> {
let mut parts = Vec::new();
parts.push(self.identifier()?);
while let Some(&Token::Dot) = self.peek() {
self.pop()?;
parts.push(self.identifier()?);
}
Ok(parts)
}
fn plus_build_metadata(&mut self) -> Result<Option<String>, Error> {
match self.peek() {
Some(&Token::Plus) => self.pop()?,
_ => return Ok(None),
};
let mut buffer = String::new();
loop {
match self.pop() {
Err(UnexpectedEnd) => break,
Ok(Token::LeadingZero(s)) => buffer.push_str(s),
Ok(Token::AlphaNumeric(s)) => buffer.push_str(s),
Ok(Token::Numeric(s)) => buffer.push_str(&s.to_string()),
Ok(Token::Dot) => buffer.push('.'),
Ok(token) => return Err(UnexpectedToken(token.to_string())),
Err(error) => return Err(error),
}
}
if buffer.is_empty() {
Err(UnexpectedEnd)
} else {
Ok(Some(buffer))
}
}
pub fn version(&mut self) -> Result<Version, Error> {
self.skip_whitespace()?;
let major = self.numeric()?;
let minor = self
.dot_numeric()
.map_err(|_| Error::MinorVersionMissing(major))?;
let patch = self
.dot_numeric()
.map_err(|_| Error::PatchVersionMissing(major, minor))?;
let pre = self.pre()?;
let build = self.plus_build_metadata()?;
self.skip_whitespace()?;
Ok(Version {
major,
minor,
patch,
pre,
build,
})
}
pub fn range(&mut self) -> Result<PubgrubRange, Error> {
let mut range: Option<PubgrubRange> = None;
loop {
let constraint = self.range_ands_section()?;
range = Some(match range {
None => constraint,
Some(range) => range.union(&constraint),
});
if self.peek() == Some(&Token::Or) {
self.pop()?;
self.expect_whitespace()?;
} else {
break;
}
}
self.skip_whitespace()?;
range.ok_or(UnexpectedEnd)
}
fn pessimistic_version_constraint(&mut self) -> Result<PubgrubRange, Error> {
let mut included_patch = false;
let major = self.numeric()?;
let minor = self.dot_numeric()?;
let patch = match self.peek() {
Some(Token::Dot) => {
included_patch = true;
self.pop()?;
self.numeric()?
}
_ => 0,
};
let pre = self.pre()?;
let build = self.plus_build_metadata()?;
let lower = Version {
major,
minor,
patch,
pre,
build,
};
let upper = if included_patch {
lower.bump_minor()
} else {
lower.bump_major()
};
Ok(
PubgrubRange::higher_than(lower)
.intersection(&PubgrubRange::strictly_lower_than(upper)),
)
}
fn range_ands_section(&mut self) -> Result<PubgrubRange, Error> {
use Token::*;
let mut range = None;
let and = |range: Option<PubgrubRange>, constraint: PubgrubRange| {
Some(match range {
None => constraint,
Some(range) => range.intersection(&constraint),
})
};
loop {
self.skip_whitespace()?;
match self.peek() {
None => break,
Some(Numeric(_)) => range = and(range, PubgrubRange::singleton(self.version()?)),
Some(Eq) => {
self.pop()?;
range = and(range, PubgrubRange::singleton(self.version()?));
}
Some(NotEq) => {
self.pop()?;
let version = self.version()?;
let bumped = version.bump_patch();
let below = PubgrubRange::strictly_lower_than(version);
let above = PubgrubRange::higher_than(bumped);
range = and(range, below.union(&above));
}
Some(Gt) => {
self.pop()?;
range = and(
range,
PubgrubRange::higher_than(self.version()?.bump_patch()),
);
}
Some(GtEq) => {
self.pop()?;
range = and(range, PubgrubRange::higher_than(self.version()?));
}
Some(Lt) => {
self.pop()?;
range = and(range, PubgrubRange::strictly_lower_than(self.version()?));
}
Some(LtEq) => {
self.pop()?;
range = and(
range,
PubgrubRange::strictly_lower_than(self.version()?.bump_patch()),
);
}
Some(Pessimistic) => {
self.pop()?;
self.skip_whitespace()?;
range = and(range, self.pessimistic_version_constraint()?);
}
Some(_) => return Err(UnexpectedToken(self.pop()?.to_string())),
};
self.skip_whitespace()?;
if self.peek() == Some(&Token::And) {
self.pop()?;
self.expect_whitespace()?;
} else {
break;
}
}
self.skip_whitespace()?;
range.ok_or(UnexpectedEnd)
}
pub fn is_eof(&mut self) -> bool {
self.c1.is_none()
}
pub fn tail(&mut self) -> Result<Vec<Token<'input>>, Error> {
let mut out = Vec::new();
if let Some(t) = self.c1.take() {
out.push(t);
}
for t in self.lexer.by_ref() {
out.push(t?);
}
Ok(out)
}
}