Support wildcards to fix remaining tests

This commit is contained in:
Paul Horn 2021-02-28 23:23:06 +01:00
parent d610e9954f
commit 5d9ce0d93a
No known key found for this signature in database
GPG key ID: DCD4B83C38219A89

View file

@ -4,21 +4,25 @@
Lenient parser or Semantic Version ranges. Lenient parser or Semantic Version ranges.
*/ */
use lenient_semver_parser::{parse_partial, VersionBuilder}; use lenient_semver_parser::{parse_partial, ErrorKind, VersionBuilder};
use lenient_version::Version; use lenient_version::Version;
pub fn parse<'a>(input: &'a str) -> Result<RangeSet<'a>, Box<dyn std::error::Error + 'a>> { pub fn parse<'a>(input: &'a str) -> Result<RangeSet<'a>, Box<dyn std::error::Error + 'a>> {
parse_compat(input, Compat::Npm) parse_compat(input, Compat::Npm)
} }
pub fn parse_cargp<'a>(input: &'a str) -> Result<RangeSet<'a>, Box<dyn std::error::Error + 'a>> {
parse_compat(input, Compat::Cargo)
}
pub fn parse_npm<'a>(input: &'a str) -> Result<RangeSet<'a>, Box<dyn std::error::Error + 'a>> { pub fn parse_npm<'a>(input: &'a str) -> Result<RangeSet<'a>, Box<dyn std::error::Error + 'a>> {
parse_compat(input, Compat::Npm) parse_compat(input, Compat::Npm)
} }
pub fn parse_compat<'a>( pub fn parse_compat<'input>(
mut input: &'a str, mut input: &'input str,
compat: Compat, compat: Compat,
) -> Result<RangeSet<'a>, Box<dyn std::error::Error + 'a>> { ) -> Result<RangeSet<'input>, Box<dyn std::error::Error + 'input>> {
let mut ranges = vec![]; let mut ranges = vec![];
let mut operations: Vec<Operation> = vec![]; let mut operations: Vec<Operation> = vec![];
let mut allow_chain_operators = false; let mut allow_chain_operators = false;
@ -57,7 +61,7 @@ pub fn parse_compat<'a>(
_ => (Op::Nothing, input), _ => (Op::Nothing, input),
}; };
let (version, remainder) = parse_partial::<VersionQualifier<'a>>(next_input)?; let (version, remainder) = parse_next_version(next_input)?;
let op = match op { let op = match op {
Op::Nothing => Operation::Default(version), Op::Nothing => Operation::Default(version),
Op::Eq => Operation::Apply(Operator::Eq, version), Op::Eq => Operation::Apply(Operator::Eq, version),
@ -111,6 +115,24 @@ pub fn parse_compat<'a>(
Ok(RangeSet { ranges }) Ok(RangeSet { ranges })
} }
#[inline]
fn parse_next_version<'input>(
input: &'input str,
) -> Result<(VersionQualifier<'input>, &'input str), lenient_semver_parser::Error<'input>> {
match parse_partial::<VersionQualifier<'input>>(input) {
Ok(v) => Ok(v),
Err(err) => {
if err.error_kind() == ErrorKind::UnexpectedInput {
if matches!(err.erroneous_input(), "x" | "X" | "*" | "?") {
let span = err.error_span();
return Ok((VersionQualifier::empty(), &input[span.end..]));
}
}
Err(err)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RangeSet<'input> { pub struct RangeSet<'input> {
ranges: Vec<Range<'input>>, ranges: Vec<Range<'input>>,
@ -165,6 +187,16 @@ enum Operator {
Lte, Lte,
} }
impl Operator {
fn or_if_eq(self, other: Self) -> Self {
if self == Self::Eq {
other
} else {
self
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Op { enum Op {
Nothing, Nothing,
@ -237,6 +269,22 @@ impl<'input> VersionQualifier<'input> {
build: Vec::new(), build: Vec::new(),
} }
} }
fn to_version<F>(self, mut f: F) -> Version<'input>
where
F: for<'a> FnMut(&'a mut Version<'input>),
{
let mut version = Version {
major: self.major.into(),
minor: self.minor.into(),
patch: self.patch.into(),
additional: self.additional.into_iter().map(u64::from).collect(),
pre: self.pre,
build: self.build,
};
f(&mut version);
version
}
} }
impl<'input> VersionBuilder<'input> for VersionQualifier<'input> { impl<'input> VersionBuilder<'input> for VersionQualifier<'input> {
@ -301,20 +349,50 @@ impl<'input> Operation<'input> {
Compat::Cargo => Operation::Caret(v).into_versions(compat), Compat::Cargo => Operation::Caret(v).into_versions(compat),
Compat::Npm => Operation::Apply(Operator::Eq, v).into_versions(compat), Compat::Npm => Operation::Apply(Operator::Eq, v).into_versions(compat),
}, },
Operation::Apply(op, v) => ( Operation::Apply(op, v) => match v.major {
Comparator { VersionNumber::Wildcard | VersionNumber::Unspecified => (
op, Comparator {
version: Version { op: op.or_if_eq(Operator::Gte),
major: v.major.into(), version: v.to_version(|v| v.set_major(0)),
minor: v.minor.into(), },
patch: v.patch.into(), None,
additional: v.additional.into_iter().map(u64::from).collect(), ),
pre: v.pre, VersionNumber::Num(major) => match v.minor {
build: v.build, VersionNumber::Unspecified | VersionNumber::Wildcard => {
let min = Comparator {
op: op.or_if_eq(Operator::Gte),
version: v.to_version(|v| v.set_minor(0)),
};
let max = major
.checked_add(1)
.map(|m| Comparator::lt(Version::new(m, 0, 0)));
(min, max)
}
VersionNumber::Num(minor) => match v.patch {
VersionNumber::Unspecified | VersionNumber::Wildcard => {
let min = Comparator {
op: op.or_if_eq(Operator::Gte),
version: v.to_version(|v| v.set_patch(0)),
};
let max = minor
.checked_add(1)
.map(|m| Comparator::lt(Version::new(major, m, 0)));
(min, max)
}
VersionNumber::Num(_) => (
Comparator {
op,
version: v.to_version(|_| ()),
},
None,
),
}, },
}, },
None, },
),
Operation::Tilde(v) => match v.segment { Operation::Tilde(v) => match v.segment {
Segment::Empty => (Comparator::gte(Version::new(0, 0, 0)), None), Segment::Empty => (Comparator::gte(Version::new(0, 0, 0)), None),
Segment::Major => { Segment::Major => {
@ -326,50 +404,17 @@ impl<'input> Operation<'input> {
.map(|m| Comparator::lt(Version::new(m, 0, 0))), .map(|m| Comparator::lt(Version::new(m, 0, 0))),
) )
} }
Segment::Minor => { Segment::Minor | Segment::Patch | Segment::Additional => {
let major = u64::from(v.major); let major = u64::from(v.major);
let minor = u64::from(v.minor); let minor = u64::from(v.minor);
let version = v.to_version(|_| ());
( (
Comparator::gte(Version::new(major, minor, 0)), Comparator::gte(version),
minor minor
.checked_add(1) .checked_add(1)
.map(|m| Comparator::lt(Version::new(major, m, 0))), .map(|m| Comparator::lt(Version::new(major, m, 0))),
) )
} }
Segment::Patch => {
let major = u64::from(v.major);
let minor = u64::from(v.minor);
let patch = u64::from(v.patch);
(
Comparator::gte(Version::new(major, minor, patch)),
minor
.checked_add(1)
.map(|m| Comparator::lt(Version::new(major, minor, m))),
)
}
Segment::Additional => {
let major = u64::from(v.major);
let minor = u64::from(v.minor);
let patch = u64::from(v.patch);
let mut min = Version::new(major, minor, patch);
min.additional = v.additional.into_iter().map(u64::from).collect();
let max = min
.additional
.last()
.expect("empty additional")
.checked_add(1)
.map(|m| {
let mut add = min.additional.clone();
*add.last_mut().expect("empty additional") = m;
let mut max = Version::new(major, minor, patch);
max.additional = add;
Comparator::lt(max)
});
(Comparator::gte(min), max)
}
Segment::PreRelease => { Segment::PreRelease => {
let min = Version { let min = Version {
major: u64::from(v.major), major: u64::from(v.major),
@ -611,17 +656,7 @@ mod tests {
#[test] #[test]
fn test_x_4() { fn test_x_4() {
assert_eq!( assert_eq!(parse("").unwrap(), RangeSet { ranges: vec![] });
parse("").unwrap(),
RangeSet {
ranges: vec![Range {
comparators: vec![Comparator {
op: Operator::Gte,
version: Version::new(0, 0, 0)
},]
}]
}
);
} }
#[test] #[test]