use compact_str::format_compact;
use semver::{Prerelease, Version, VersionReq};
pub trait VersionReqExt {
fn is_latest_compatible(&self, version: &Version) -> bool;
fn parse_from_cli(str: &str) -> Result<Self, semver::Error>
where
Self: Sized;
}
impl VersionReqExt for VersionReq {
fn is_latest_compatible(&self, version: &Version) -> bool {
if !self.matches(version) {
return false;
}
let bumped_version = Version::new(version.major, version.minor, version.patch + 1);
if self.matches(&bumped_version) {
return false;
}
let pre = &version.pre;
if !pre.is_empty() {
let bumped_pre = format_compact!("{}.1", pre.as_str());
let bumped_version = Version {
major: version.major,
minor: version.minor,
patch: version.patch,
pre: Prerelease::new(&bumped_pre).unwrap(),
build: Default::default(),
};
if self.matches(&bumped_version) {
return false;
}
}
true
}
fn parse_from_cli(version: &str) -> Result<Self, semver::Error> {
if version
.chars()
.next()
.map(|ch| ch.is_ascii_digit())
.unwrap_or(false)
{
format_compact!("={version}").parse()
} else {
version.parse()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.0.1").unwrap()));
assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.1.1").unwrap()));
assert!(!VersionReq::STAR.is_latest_compatible(&Version::parse("0.1.1-alpha").unwrap()));
assert!(!VersionReq::parse("^0.1")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.99").unwrap()));
assert!(VersionReq::parse("=0.1.0")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0").unwrap()));
assert!(VersionReq::parse("=0.1.0-alpha")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap()));
assert!(!VersionReq::parse(">=0.1.0-alpha")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap()));
}
}