Rename lib to binstalk (#361)

This commit is contained in:
Félix Saparelli 2022-09-10 18:44:18 +12:00 committed by GitHub
parent a94d83f0d5
commit e25aa50ec9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 25 additions and 25 deletions

View file

@ -0,0 +1,112 @@
use std::{fmt, str::FromStr};
use compact_str::CompactString;
use itertools::Itertools;
use semver::{Error, VersionReq};
use super::version_ext::VersionReqExt;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CrateName {
pub name: CompactString,
pub version_req: Option<VersionReq>,
}
impl fmt::Display for CrateName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(version) = &self.version_req {
write!(f, "@{version}")?;
}
Ok(())
}
}
impl FromStr for CrateName {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(if let Some((name, version)) = s.split_once('@') {
CrateName {
name: name.into(),
version_req: Some(VersionReq::parse_from_cli(version)?),
}
} else {
CrateName {
name: s.into(),
version_req: None,
}
})
}
}
impl CrateName {
pub fn dedup(crate_names: &[Self]) -> impl Iterator<Item = Self> {
let mut crate_names = crate_names.to_vec();
crate_names.sort_by(|x, y| x.name.cmp(&y.name));
crate_names.into_iter().coalesce(|previous, current| {
if previous.name == current.name {
Ok(current)
} else {
Err((previous, current))
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_dedup {
([ $( ( $input_name:expr, $input_version:expr ) ),* ], [ $( ( $output_name:expr, $output_version:expr ) ),* ]) => {
let input_crate_names = [$( CrateName {
name: $input_name.into(),
version_req: Some($input_version.parse().unwrap())
}, )*];
let mut output_crate_names: Vec<CrateName> = vec![$( CrateName {
name: $output_name.into(), version_req: Some($output_version.parse().unwrap())
}, )*];
output_crate_names.sort_by(|x, y| x.name.cmp(&y.name));
let crate_names: Vec<_> = CrateName::dedup(&input_crate_names).collect();
assert_eq!(crate_names, output_crate_names);
};
}
#[test]
fn test_dedup() {
// Base case 0: Empty input
assert_dedup!([], []);
// Base case 1: With only one input
assert_dedup!([("a", "1")], [("a", "1")]);
// Base Case 2: Only has duplicate names
assert_dedup!([("a", "1"), ("a", "2")], [("a", "2")]);
// Complex Case 0: Having two crates
assert_dedup!(
[("a", "10"), ("b", "3"), ("a", "0"), ("b", "0"), ("a", "1")],
[("a", "1"), ("b", "0")]
);
// Complex Case 1: Having three crates
assert_dedup!(
[
("d", "1.1"),
("a", "10"),
("b", "3"),
("d", "230"),
("a", "0"),
("b", "0"),
("a", "1"),
("d", "23")
],
[("a", "1"), ("b", "0"), ("d", "23")]
);
}
}

View file

@ -0,0 +1,99 @@
use compact_str::format_compact;
use semver::{Prerelease, Version, VersionReq};
/// Extension trait for [`VersionReq`].
pub trait VersionReqExt {
/// Return `true` if `self.matches(version)` returns `true`
/// and the `version` is the latest one acceptable by `self`.
fn is_latest_compatible(&self, version: &Version) -> bool;
/// Parse from CLI option.
///
/// Notably, a bare version is treated as if preceded by `=`, not by `^` as in Cargo.toml
/// dependencies.
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;
}
// Test if bumping patch will be accepted
let bumped_version = Version::new(version.major, version.minor, version.patch + 1);
if self.matches(&bumped_version) {
return false;
}
// Test if bumping prerelease will be accepted if version has one.
let pre = &version.pre;
if !pre.is_empty() {
// Bump pre by appending random number to the end.
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() {
// Test star
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()));
// Test ^x.y.z
assert!(!VersionReq::parse("^0.1")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.99").unwrap()));
// Test =x.y.z
assert!(VersionReq::parse("=0.1.0")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0").unwrap()));
// Test =x.y.z-alpha
assert!(VersionReq::parse("=0.1.0-alpha")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap()));
// Test >=x.y.z-alpha
assert!(!VersionReq::parse(">=0.1.0-alpha")
.unwrap()
.is_latest_compatible(&Version::parse("0.1.0-alpha").unwrap()));
}
}