From 5d1e368c83ce0c74a2adf828b9b083c888cac933 Mon Sep 17 00:00:00 2001 From: Martin Geisler Date: Sun, 28 Nov 2021 22:02:58 +0100 Subject: [PATCH] Check version numbers in `=1.2.3` and `>1.2.3`, and `>=1.2.3` Before, a dependency specified as ```toml [dependencies] foo = ">= 1.2.3, < 2.0" ``` was ignored since it was deemed too complicated to check versions requirements with multiple comparators. We now handle such cases by checking every comparator individually. Comparators which form a lower-bound for the version is checked. This means that version requirements of the form * `1.2` and `^1.2` * `~1.2` * `=1.2` * `>1.2` * `>=1.2` * `1.2.*` are all checked for compatibility with the actual crate version. If the crate version is higher than the version requirement, an error is returned. Upper-build version requirements such as `<2.0` are ignored like before. --- src/helpers.rs | 109 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index f5ca15e..248f015 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -49,44 +49,41 @@ pub fn version_matches_request( request: &semver::VersionReq, ) -> Result<()> { use semver::Op; - if request.comparators.len() != 1 { - // Can only handle simple dependencies - return Ok(()); - } - let comparator = &request.comparators[0]; - match comparator.op { - Op::Tilde | Op::Caret => { - if comparator.major != version.major { - return Err(format!( - "expected major version {}, found {}", - version.major, comparator.major, - )); - } - if let Some(minor) = comparator.minor { - if minor != version.minor { + for comparator in &request.comparators { + match comparator.op { + Op::Tilde | Op::Caret | Op::Exact | Op::Greater | Op::GreaterEq | Op::Wildcard => { + if comparator.major != version.major { return Err(format!( - "expected minor version {}, found {}", - version.minor, minor + "expected major version {}, found {}", + version.major, comparator.major, )); } - } - if let Some(patch) = comparator.patch { - if patch != version.patch { + if let Some(minor) = comparator.minor { + if minor != version.minor { + return Err(format!( + "expected minor version {}, found {}", + version.minor, minor + )); + } + } + if let Some(patch) = comparator.patch { + if patch != version.patch { + return Err(format!( + "expected patch version {}, found {}", + version.patch, patch + )); + } + } + if comparator.pre != version.pre { return Err(format!( - "expected patch version {}, found {}", - version.patch, patch + "expected pre-release \"{}\", found \"{}\"", + version.pre, comparator.pre )); } } - if comparator.pre != version.pre { - return Err(format!( - "expected pre-release \"{}\", found \"{}\"", - version.pre, comparator.pre - )); - } + _ => {} // We cannot check other operators. } - _ => return Ok(()), // We cannot check other operators. } Ok(()) @@ -109,6 +106,9 @@ mod tests { let version = Version::parse("1.2.3").unwrap(); let request = VersionReq::parse("1.2.3").unwrap(); assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse("1.2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); } #[test] @@ -116,6 +116,9 @@ mod tests { let version = Version::parse("1.2.3").unwrap(); let request = VersionReq::parse("^1.2.3").unwrap(); assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse("^1.2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); } #[test] @@ -123,6 +126,49 @@ mod tests { let version = Version::parse("1.2.3").unwrap(); let request = VersionReq::parse("~1.2.3").unwrap(); assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse("~1.2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); + } + + #[test] + fn exact() { + let version = Version::parse("1.2.3").unwrap(); + let request = VersionReq::parse("=1.2.3").unwrap(); + assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse("=1.2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); + } + + #[test] + fn greater_or_equal() { + let version = Version::parse("1.2.3").unwrap(); + let request = VersionReq::parse(">=1.2.3").unwrap(); + assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse(">=1.2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); + } + + #[test] + fn wildcard() { + let version = Version::parse("1.2.3").unwrap(); + let request = VersionReq::parse("1.2.*").unwrap(); + assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse("1.3.*").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); + } + + #[test] + fn greater() { + let version = Version::parse("1.2.3").unwrap(); + let request = VersionReq::parse(">1.2.3").unwrap(); + assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse(">1.2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); } #[test] @@ -140,10 +186,13 @@ mod tests { } #[test] - fn multiple_predicates() { + fn multiple_comparators() { let version = Version::parse("1.2.3").unwrap(); let request = VersionReq::parse(">= 1.2.3, < 2.0").unwrap(); assert_eq!(version_matches_request(&version, &request), Ok(())); + + let request = VersionReq::parse(">= 1.2.0, < 2.0").unwrap(); + assert!(version_matches_request(&version, &request).is_err()); } #[test]