From 38c8bc8cf297640a27a39eb1501af871f04f234c Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 12 Jul 2022 16:47:44 +1000 Subject: [PATCH 1/6] Impl new type `helpes::CrateName` Signed-off-by: Jiahao XU --- src/helpers.rs | 3 +++ src/helpers/crate_name.rs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/helpers/crate_name.rs diff --git a/src/helpers.rs b/src/helpers.rs index b7614e2f..254119b8 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -34,6 +34,9 @@ pub use path_ext::*; mod tls_version; pub use tls_version::TLSVersion; +mod crate_name; +pub use crate_name::CrateName; + /// Load binstall metadata from the crate `Cargo.toml` at the provided path pub fn load_manifest_path>( manifest_path: P, diff --git a/src/helpers/crate_name.rs b/src/helpers/crate_name.rs new file mode 100644 index 00000000..b7eb1077 --- /dev/null +++ b/src/helpers/crate_name.rs @@ -0,0 +1,39 @@ +use std::convert::Infallible; +use std::fmt; +use std::str::FromStr; + +#[derive(Debug)] +pub struct CrateName { + pub name: String, + pub version: Option, +} + +impl fmt::Display for CrateName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name)?; + + if let Some(version) = &self.version { + write!(f, "@{version}")?; + } + + Ok(()) + } +} + +impl FromStr for CrateName { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(if let Some((name, version)) = s.split_once('@') { + CrateName { + name: name.to_string(), + version: Some(version.to_string()), + } + } else { + CrateName { + name: s.to_string(), + version: None, + } + }) + } +} From 41961ce218748ed6efe6c9103c5917c2dfc76de1 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 12 Jul 2022 17:03:12 +1000 Subject: [PATCH 2/6] Add new variant `BinstallError::DuplicateVersionReq` Signed-off-by: Jiahao XU --- src/errors.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/errors.rs b/src/errors.rs index 4c7e5cad..4db5cdd1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -182,6 +182,12 @@ pub enum BinstallError { help("You specified `--version {req}` but the package resolved that to '{ver}'.\nUse `--version '={req}'` if you want an exact match.") )] VersionWarning { ver: String, req: String }, + + /// This occurs when you specified `--version` while also using + /// form `$crate_name@$ver` tp specify version requirements. + #[error("duplicate version requirements")] + #[diagnostic(severity(error), code(binstall::version::requirement))] + DuplicateVersionReq, } impl BinstallError { @@ -209,6 +215,7 @@ impl BinstallError { VersionMismatch { .. } => 82, VersionUnavailable { .. } => 83, VersionWarning { .. } => unimplemented!("BUG: warnings do not terminate"), + DuplicateVersionReq => 84, }; // reserved codes From ff0bd4d9484a9671c9aa93be4baaf79f02a41a97 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 12 Jul 2022 17:08:54 +1000 Subject: [PATCH 3/6] Support specifing ver via `crate_name@version` Signed-off-by: Jiahao XU --- src/main.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 24cf6964..73512c34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,14 +36,14 @@ struct Options { /// /// This must be a crates.io package name. #[clap(value_name = "crate")] - name: String, + crate_name: CrateName, /// Semver filter to select the package version to install. /// /// This is in Cargo.toml dependencies format: `--version 1.2.3` is equivalent to /// `--version "^1.2.3"`. Use `=1.2.3` to install a specific version. - #[clap(long, default_value = "*")] - version: String, + #[clap(long)] + version: Option, /// Override binary target set. /// @@ -237,26 +237,33 @@ async fn entry() -> Result<()> { .map_err(BinstallError::from) .wrap_err("Creating a temporary directory failed.")?; - info!("Installing package: '{}'", opts.name); + info!("Installing package: '{}'", opts.crate_name); + + let version = match (&opts.crate_name.version, &opts.version) { + (Some(version), None) => version.to_string(), + (None, Some(version)) => version.to_string(), + (Some(_), Some(_)) => Err(BinstallError::DuplicateVersionReq)?, + (None, None) => "*".to_string(), + }; // Fetch crate via crates.io, git, or use a local manifest path // TODO: work out which of these to do based on `opts.name` // TODO: support git-based fetches (whole repo name rather than just crate name) let manifest = match opts.manifest_path.clone() { Some(manifest_path) => load_manifest_path(manifest_path.join("Cargo.toml"))?, - None => fetch_crate_cratesio(&client, &opts.name, &opts.version).await?, + None => fetch_crate_cratesio(&client, &opts.crate_name.name, &version).await?, }; let package = manifest.package.unwrap(); - let is_plain_version = semver::Version::from_str(&opts.version).is_ok(); - if is_plain_version && package.version != opts.version { + let is_plain_version = semver::Version::from_str(&version).is_ok(); + if is_plain_version && package.version != version { warn!("Warning!"); eprintln!( "{:?}", miette::Report::new(BinstallError::VersionWarning { ver: package.version.clone(), - req: opts.version.clone() + req: version.clone(), }) ); @@ -312,7 +319,9 @@ async fn entry() -> Result<()> { meta.merge(&cli_overrides); // Generate temporary binary path - let bin_path = temp_dir.path().join(format!("bin-{}", opts.name)); + let bin_path = temp_dir + .path() + .join(format!("bin-{}", opts.crate_name.name)); debug!("Using temporary binary path: {}", bin_path.display()); let bin_files = collect_bin_files( @@ -363,6 +372,7 @@ async fn entry() -> Result<()> { opts, package, temp_dir, + version, &bin_path, &bin_files, ) @@ -438,6 +448,7 @@ async fn install_from_package( opts: Options, package: Package, temp_dir: TempDir, + version: String, bin_path: &Path, bin_files: &[bins::BinFile], ) -> Result<()> { @@ -478,7 +489,7 @@ async fn install_from_package( } let cvs = metafiles::CrateVersionSource { - name: opts.name, + name: opts.crate_name.name, version: package.version.parse().into_diagnostic()?, source: metafiles::Source::Registry( url::Url::parse("https://github.com/rust-lang/crates.io-index").unwrap(), @@ -521,7 +532,7 @@ async fn install_from_package( c2.insert( cvs.clone(), metafiles::v2::CrateInfo { - version_req: Some(opts.version), + version_req: Some(version), bins, profile: "release".into(), target: fetcher.target().to_string(), From e2207f7b59b98a4e608319cb3096f51f5ab81904 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 12 Jul 2022 17:12:01 +1000 Subject: [PATCH 4/6] Add `diagnostic(help)` to `DuplicateVersionReq` Signed-off-by: Jiahao XU --- src/errors.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/errors.rs b/src/errors.rs index 4db5cdd1..286a3c9a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -186,7 +186,11 @@ pub enum BinstallError { /// This occurs when you specified `--version` while also using /// form `$crate_name@$ver` tp specify version requirements. #[error("duplicate version requirements")] - #[diagnostic(severity(error), code(binstall::version::requirement))] + #[diagnostic( + severity(error), + code(binstall::version::requirement), + help("Remove the `--version req` or simply use `$crate_name`") + )] DuplicateVersionReq, } From c87941211cc782660e737b491dbbc9f08898cbce Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 12 Jul 2022 18:33:31 +1000 Subject: [PATCH 5/6] Treat `1.2.3` as `=1.2.3` to match `cargo-install` Signed-off-by: Jiahao XU --- src/errors.rs | 3 +-- src/main.rs | 28 ++++++++++------------------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 286a3c9a..855a23b5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -147,8 +147,7 @@ pub enum BinstallError { /// /// This may be the case when using the `--version` option. /// - /// Note that using `--version 1.2.3` is interpreted as the requirement `^1.2.3` as per - /// Cargo.toml rules. If you want the exact version 1.2.3, use `--version '=1.2.3'`. + /// Note that using `--version 1.2.3` is interpreted as the requirement `=1.2.3`. /// /// - Code: `binstall::version::mismatch` /// - Exit: 82 diff --git a/src/main.rs b/src/main.rs index 73512c34..b5a8af31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ use std::{ ffi::OsString, path::{Path, PathBuf}, process::{ExitCode, Termination}, - str::FromStr, time::{Duration, Instant}, }; @@ -239,13 +238,22 @@ async fn entry() -> Result<()> { info!("Installing package: '{}'", opts.crate_name); - let version = match (&opts.crate_name.version, &opts.version) { + let mut version = match (&opts.crate_name.version, &opts.version) { (Some(version), None) => version.to_string(), (None, Some(version)) => version.to_string(), (Some(_), Some(_)) => Err(BinstallError::DuplicateVersionReq)?, (None, None) => "*".to_string(), }; + if version + .chars() + .next() + .map(|ch| ch.is_ascii_digit()) + .unwrap_or(false) + { + version.insert(0, '='); + } + // Fetch crate via crates.io, git, or use a local manifest path // TODO: work out which of these to do based on `opts.name` // TODO: support git-based fetches (whole repo name rather than just crate name) @@ -256,22 +264,6 @@ async fn entry() -> Result<()> { let package = manifest.package.unwrap(); - let is_plain_version = semver::Version::from_str(&version).is_ok(); - if is_plain_version && package.version != version { - warn!("Warning!"); - eprintln!( - "{:?}", - miette::Report::new(BinstallError::VersionWarning { - ver: package.version.clone(), - req: version.clone(), - }) - ); - - if !opts.dry_run { - uithread.confirm().await?; - } - } - let (mut meta, binaries) = ( package .metadata From aecc474f291b0a2cbc4ff4a9363f276632ae1847 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 12 Jul 2022 18:34:20 +1000 Subject: [PATCH 6/6] Rm unused `BinstallError::VersionWarning` Signed-off-by: Jiahao XU --- src/errors.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 855a23b5..ec19f29a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -166,22 +166,6 @@ pub enum BinstallError { v: semver::Version, }, - /// Warning: The resolved version may not be what was meant. - /// - /// This occurs when using the `--version` option with a bare version, like `--version 1.2.3`. - /// That is parsed as the semver requirement `^1.2.3`, but the user may have expected that to - /// be an exact version (which should be specified with `--version '=1.2.3'`. - /// - /// - Code: `binstall::version::warning` - /// - Exit: none (runtime warning only) - #[error("version semantic mismatch: {ver} <> {req}")] - #[diagnostic( - severity(warning), - code(binstall::version::warning), - help("You specified `--version {req}` but the package resolved that to '{ver}'.\nUse `--version '={req}'` if you want an exact match.") - )] - VersionWarning { ver: String, req: String }, - /// This occurs when you specified `--version` while also using /// form `$crate_name@$ver` tp specify version requirements. #[error("duplicate version requirements")] @@ -217,7 +201,6 @@ impl BinstallError { VersionReq { .. } => 81, VersionMismatch { .. } => 82, VersionUnavailable { .. } => 83, - VersionWarning { .. } => unimplemented!("BUG: warnings do not terminate"), DuplicateVersionReq => 84, };