use monch::*;
use crate::CowVec;
use crate::Partial;
use crate::VersionBoundKind;
use crate::VersionPreOrBuild;
use crate::VersionRange;
use crate::XRange;
pub fn logical_or(input: &str) -> ParseResult<'_, &str> {
delimited(skip_whitespace, tag("||"), skip_whitespace)(input)
}
pub fn logical_and(input: &str) -> ParseResult<'_, &str> {
delimited(skip_whitespace, tag("&&"), skip_whitespace)(input)
}
pub fn partial<'a>(
xr: impl Fn(&'a str) -> ParseResult<'a, XRange>,
) -> impl Fn(&'a str) -> ParseResult<'a, Partial> {
move |input| {
let (input, major) = xr(input)?;
let (input, maybe_minor) = maybe(preceded(ch('.'), &xr))(input)?;
let (input, maybe_patch) = if maybe_minor.is_some() {
maybe(preceded(ch('.'), &xr))(input)?
} else {
(input, None)
};
let (input, qual) = if maybe_patch.is_some() {
maybe(qualifier)(input)?
} else {
(input, None)
};
let qual = qual.unwrap_or_default();
Ok((
input,
Partial {
major,
minor: maybe_minor.unwrap_or(XRange::Wildcard),
patch: maybe_patch.unwrap_or(XRange::Wildcard),
pre: qual.pre,
build: qual.build,
},
))
}
}
#[derive(Debug, Clone, Default)]
pub struct Qualifier {
pub pre: CowVec<VersionPreOrBuild>,
pub build: CowVec<VersionPreOrBuild>,
}
pub fn qualifier(input: &str) -> ParseResult<'_, Qualifier> {
let (input, pre_parts) = maybe(pre)(input)?;
let (input, build_parts) = maybe(build)(input)?;
Ok((
input,
Qualifier {
pre: pre_parts.unwrap_or_default(),
build: build_parts.unwrap_or_default(),
},
))
}
fn pre(input: &str) -> ParseResult<'_, CowVec<VersionPreOrBuild>> {
preceded(maybe(ch('-')), parts)(input)
}
fn build(input: &str) -> ParseResult<'_, CowVec<VersionPreOrBuild>> {
preceded(ch('+'), parts)(input)
}
fn parts(input: &str) -> ParseResult<'_, CowVec<VersionPreOrBuild>> {
let (rest, parts) = separated_fold(
part,
ch('.'),
CowVec::<VersionPreOrBuild>::new(),
|mut acc, s| {
acc.push(VersionPreOrBuild::from_str(s));
acc
},
)(input)?;
if parts.is_empty() {
return ParseError::backtrace();
}
Ok((rest, parts))
}
fn part(input: &str) -> ParseResult<'_, &str> {
let (rest, result) =
take_while_byte(|b| b.is_ascii_alphanumeric() || b == b'-')(input)?;
if result.is_empty() {
return ParseError::backtrace();
}
Ok((rest, result))
}
#[derive(Debug, Clone, Copy)]
pub enum PrimitiveKind {
GreaterThan,
LessThan,
GreaterThanOrEqual,
LessThanOrEqual,
Equal,
}
pub fn primitive_kind(input: &str) -> ParseResult<'_, PrimitiveKind> {
or5(
map(tag(">="), |_| PrimitiveKind::GreaterThanOrEqual),
map(tag("<="), |_| PrimitiveKind::LessThanOrEqual),
map(ch('<'), |_| PrimitiveKind::LessThan),
map(ch('>'), |_| PrimitiveKind::GreaterThan),
map(ch('='), |_| PrimitiveKind::Equal),
)(input)
}
#[derive(Debug, Clone)]
pub struct Primitive {
pub kind: PrimitiveKind,
pub partial: Partial,
}
impl Primitive {
pub fn into_version_range(self) -> VersionRange {
let partial = self.partial;
match self.kind {
PrimitiveKind::Equal => partial.as_equal_range(),
PrimitiveKind::GreaterThan => {
partial.as_greater_than(VersionBoundKind::Exclusive)
}
PrimitiveKind::GreaterThanOrEqual => {
partial.as_greater_than(VersionBoundKind::Inclusive)
}
PrimitiveKind::LessThan => {
partial.as_less_than(VersionBoundKind::Exclusive)
}
PrimitiveKind::LessThanOrEqual => {
partial.as_less_than(VersionBoundKind::Inclusive)
}
}
}
}
pub fn primitive<'a>(
partial: impl Fn(&'a str) -> ParseResult<'a, Partial>,
) -> impl Fn(&'a str) -> ParseResult<'a, Primitive> {
move |input| {
let (input, kind) = primitive_kind(input)?;
let (input, _) = skip_whitespace(input)?;
let (input, partial) = partial(input)?;
Ok((input, Primitive { kind, partial }))
}
}