From c418c2dbbebfbdca5ee0ef484c300281cdeaeece Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 12:44:54 +1000 Subject: [PATCH 01/12] Optimize arg parsing: Avoid O(n) `Vec::remove` Signed-off-by: Jiahao XU --- src/main.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 51847dcb..a11e72f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -186,9 +186,20 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { // `cargo run -- --help` gives ["target/debug/cargo-binstall", "--help"] // `cargo binstall --help` gives ["/home/ryan/.cargo/bin/cargo-binstall", "binstall", "--help"] let mut args: Vec = std::env::args_os().collect(); - if args.len() > 1 && args[1] == "binstall" { - args.remove(1); - } + let args = if args.len() > 1 && args[1] == "binstall" { + // Equivalent to + // + // args.remove(1); + // + // But is O(1) + args.swap(0, 1); + let mut args = args.into_iter(); + drop(args.next().unwrap()); + + args + } else { + args.into_iter() + }; // Load options let mut opts = Options::parse_from(args); From 6180e9ec3e8d9f2263a40b119ed6b94453d15ba7 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 13:00:58 +1000 Subject: [PATCH 02/12] Add comment in fn `resolve` Signed-off-by: Jiahao XU --- src/binstall/resolve.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/binstall/resolve.rs b/src/binstall/resolve.rs index 33592e60..91ad1f57 100644 --- a/src/binstall/resolve.rs +++ b/src/binstall/resolve.rs @@ -90,6 +90,7 @@ pub async fn resolve( (None, None) => "*".to_string(), }; + // Treat 0.1.2 as =0.1.2 if version .chars() .next() From dc8d8ccd8850566276e00d9f7fddf31d54c9c378 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 13:06:34 +1000 Subject: [PATCH 03/12] Optimize `MultiFetcher`: Start `check`ing ASAP Signed-off-by: Jiahao XU --- src/fetchers.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/fetchers.rs b/src/fetchers.rs index 237a2b85..dc60dfc6 100644 --- a/src/fetchers.rs +++ b/src/fetchers.rs @@ -47,30 +47,21 @@ pub struct Data { pub meta: PkgMeta, } +type FetcherJoinHandle = AutoAbortJoinHandle>; + #[derive(Default)] -pub struct MultiFetcher { - fetchers: Vec>, -} +pub struct MultiFetcher(Vec<(Arc, FetcherJoinHandle)>); impl MultiFetcher { pub fn add(&mut self, fetcher: Arc) { - self.fetchers.push(fetcher); + self.0.push(( + fetcher.clone(), + AutoAbortJoinHandle::new(tokio::spawn(async move { fetcher.check().await })), + )); } - pub async fn first_available(&self) -> Option> { - let handles: Vec<_> = self - .fetchers - .iter() - .cloned() - .map(|fetcher| { - ( - fetcher.clone(), - AutoAbortJoinHandle::new(tokio::spawn(async move { fetcher.check().await })), - ) - }) - .collect(); - - for (fetcher, handle) in handles { + pub async fn first_available(self) -> Option> { + for (fetcher, handle) in self.0 { match handle.await { Ok(Ok(true)) => return Some(fetcher), Ok(Ok(false)) => (), From 305a4e4c306c1e665c4d7b76635e2a6043709bc8 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 13:07:20 +1000 Subject: [PATCH 04/12] Improve err msg in `MultiFetcher::first_available` Signed-off-by: Jiahao XU --- src/fetchers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fetchers.rs b/src/fetchers.rs index dc60dfc6..46917e0f 100644 --- a/src/fetchers.rs +++ b/src/fetchers.rs @@ -74,7 +74,7 @@ impl MultiFetcher { } Err(join_err) => { debug!( - "Error while checking fetcher {}: {}", + "Error while joining the task that checks the fetcher {}: {}", fetcher.source_name(), join_err ); From 21eac33e1f6f7b3c205d3be8c707160125e28ec3 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 13:13:07 +1000 Subject: [PATCH 05/12] Optimize: Create new fn `helpers::cargo_home` that caches return value of `home::cargo_home` Signed-off-by: Jiahao XU --- src/helpers.rs | 10 ++++++++++ src/metafiles/v1.rs | 3 ++- src/metafiles/v2.rs | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 3dc2de96..3381c839 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,12 +1,14 @@ use std::fmt::Debug; use std::fs; use std::io; +use std::ops; use std::path::{Path, PathBuf}; use bytes::Bytes; use cargo_toml::Manifest; use futures_util::stream::Stream; use log::debug; +use once_cell::sync::OnceCell; use reqwest::{tls, Client, ClientBuilder, Method, Response}; use serde::Serialize; use tempfile::NamedTempFile; @@ -40,6 +42,14 @@ pub use tls_version::TLSVersion; mod crate_name; pub use crate_name::CrateName; +pub fn cargo_home() -> Result<&'static Path, io::Error> { + static CARGO_HOME: OnceCell = OnceCell::new(); + + CARGO_HOME + .get_or_try_init(home::cargo_home) + .map(ops::Deref::deref) +} + pub async fn await_task(task: tokio::task::JoinHandle>) -> miette::Result { match task.await { Ok(res) => res, diff --git a/src/metafiles/v1.rs b/src/metafiles/v1.rs index 1180814a..19f0ab8f 100644 --- a/src/metafiles/v1.rs +++ b/src/metafiles/v1.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use super::CrateVersionSource; +use crate::cargo_home; #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct CratesToml { @@ -18,7 +19,7 @@ pub struct CratesToml { impl CratesToml { pub fn default_path() -> Result { - Ok(home::cargo_home()?.join(".crates.toml")) + Ok(cargo_home()?.join(".crates.toml")) } pub fn load() -> Result { diff --git a/src/metafiles/v2.rs b/src/metafiles/v2.rs index 70c4e46e..4724b1e8 100644 --- a/src/metafiles/v2.rs +++ b/src/metafiles/v2.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use super::CrateVersionSource; +use crate::cargo_home; #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Crates2Json { @@ -39,7 +40,7 @@ pub struct CrateInfo { impl Crates2Json { pub fn default_path() -> Result { - Ok(home::cargo_home()?.join(".crates2.json")) + Ok(cargo_home()?.join(".crates2.json")) } pub fn load() -> Result { From f09004b5b7f40933465d876f2e51d48f926222ae Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 14:50:51 +1000 Subject: [PATCH 06/12] Optimize: Share `crates_io_api::AsyncClient` So that the connection pool and the rate limit will be shared. Signed-off-by: Jiahao XU --- src/binstall/resolve.rs | 6 +++++- src/drivers/crates_io.rs | 24 ++++++++---------------- src/main.rs | 10 ++++++++++ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/binstall/resolve.rs b/src/binstall/resolve.rs index 91ad1f57..31b885bd 100644 --- a/src/binstall/resolve.rs +++ b/src/binstall/resolve.rs @@ -72,6 +72,7 @@ impl Resolution { } } +#[allow(clippy::too_many_arguments)] pub async fn resolve( opts: Arc, crate_name: CrateName, @@ -80,6 +81,7 @@ pub async fn resolve( temp_dir: Arc, install_path: Arc, client: Client, + crates_io_api_client: crates_io_api::AsyncClient, ) -> Result { info!("Installing package: '{}'", crate_name); @@ -105,7 +107,9 @@ pub async fn resolve( // 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, &crate_name.name, &version).await?, + None => { + fetch_crate_cratesio(&client, &crates_io_api_client, &crate_name.name, &version).await? + } }; let package = manifest.package.unwrap(); diff --git a/src/drivers/crates_io.rs b/src/drivers/crates_io.rs index a389f5d1..b7e96086 100644 --- a/src/drivers/crates_io.rs +++ b/src/drivers/crates_io.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::time::Duration; use cargo_toml::Manifest; use crates_io_api::AsyncClient; @@ -18,28 +17,21 @@ use visitor::ManifestVisitor; /// Fetch a crate Cargo.toml by name and version from crates.io pub async fn fetch_crate_cratesio( client: &Client, + crates_io_api_client: &AsyncClient, name: &str, version_req: &str, ) -> Result, BinstallError> { // Fetch / update index debug!("Looking up crate information"); - // Build crates.io api client - let api_client = AsyncClient::new( - "cargo-binstall (https://github.com/ryankurte/cargo-binstall)", - Duration::from_millis(100), - ) - .expect("bug: invalid user agent"); - // Fetch online crate information - let base_info = - api_client - .get_crate(name.as_ref()) - .await - .map_err(|err| BinstallError::CratesIoApi { - crate_name: name.into(), - err, - })?; + let base_info = crates_io_api_client + .get_crate(name.as_ref()) + .await + .map_err(|err| BinstallError::CratesIoApi { + crate_name: name.into(), + err, + })?; // Locate matching version let version_iter = diff --git a/src/main.rs b/src/main.rs index a11e72f0..df166c41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -216,6 +216,13 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { // Initialize reqwest client let client = create_reqwest_client(opts.secure, opts.min_tls_version.map(|v| v.into()))?; + // Build crates.io api client + let crates_io_api_client = crates_io_api::AsyncClient::new( + "cargo-binstall (https://github.com/ryankurte/cargo-binstall)", + Duration::from_millis(100), + ) + .expect("bug: invalid user agent"); + // Setup logging let mut log_config = ConfigBuilder::new(); log_config.add_filter_ignore("hyper".to_string()); @@ -273,6 +280,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { temp_dir_path.clone(), install_path.clone(), client.clone(), + crates_io_api_client.clone(), )) }) .collect(); @@ -308,6 +316,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { let jobserver_client = jobserver_client.clone(); let desired_targets = desired_targets.clone(); let client = client.clone(); + let crates_io_api_client = crates_io_api_client.clone(); let cli_overrides = cli_overrides.clone(); let install_path = install_path.clone(); @@ -320,6 +329,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { temp_dir_path, install_path, client, + crates_io_api_client, ) .await?; From aa88dce21530cf26b0dbbfc9bc2d5195cdef3b77 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 15:00:01 +1000 Subject: [PATCH 07/12] Add field `cli_overrides` to `binstall::Options` Signed-off-by: Jiahao XU --- src/binstall.rs | 3 +++ src/binstall/resolve.rs | 6 ++---- src/main.rs | 8 +++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/binstall.rs b/src/binstall.rs index 5de574bb..57a83e13 100644 --- a/src/binstall.rs +++ b/src/binstall.rs @@ -1,5 +1,7 @@ use std::path::PathBuf; +use crate::PkgOverride; + mod resolve; pub use resolve::*; @@ -11,4 +13,5 @@ pub struct Options { pub dry_run: bool, pub version: Option, pub manifest_path: Option, + pub cli_overrides: PkgOverride, } diff --git a/src/binstall/resolve.rs b/src/binstall/resolve.rs index 31b885bd..9e77490e 100644 --- a/src/binstall/resolve.rs +++ b/src/binstall/resolve.rs @@ -72,12 +72,10 @@ impl Resolution { } } -#[allow(clippy::too_many_arguments)] pub async fn resolve( opts: Arc, crate_name: CrateName, desired_targets: DesiredTargets, - cli_overrides: Arc, temp_dir: Arc, install_path: Arc, client: Client, @@ -136,7 +134,7 @@ pub async fn resolve( target_meta.merge(&o); } - target_meta.merge(&cli_overrides); + target_meta.merge(&opts.cli_overrides); debug!("Found metadata: {target_meta:?}"); let fetcher_data = Data { @@ -158,7 +156,7 @@ pub async fn resolve( if let Some(o) = meta.overrides.get(&fetcher_target.to_owned()).cloned() { meta.merge(&o); } - meta.merge(&cli_overrides); + meta.merge(&opts.cli_overrides); // Generate temporary binary path let bin_path = temp_dir.join(format!("bin-{}", crate_name.name)); diff --git a/src/main.rs b/src/main.rs index df166c41..4f90b51c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -203,11 +203,11 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { // Load options let mut opts = Options::parse_from(args); - let cli_overrides = Arc::new(PkgOverride { + let cli_overrides = PkgOverride { pkg_url: opts.pkg_url.take(), pkg_fmt: opts.pkg_fmt.take(), bin_dir: opts.bin_dir.take(), - }); + }; let crate_names = take(&mut opts.crate_names); if crate_names.len() > 1 && opts.manifest_path.is_some() { return Err(BinstallError::ManifestPathConflictedWithBatchInstallation.into()); @@ -265,6 +265,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { dry_run: opts.dry_run, version: opts.version.take(), manifest_path: opts.manifest_path.take(), + cli_overrides, }); let tasks: Vec<_> = if !opts.dry_run && !opts.no_confirm { @@ -276,7 +277,6 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { binstall_opts.clone(), crate_name, desired_targets.clone(), - cli_overrides.clone(), temp_dir_path.clone(), install_path.clone(), client.clone(), @@ -317,7 +317,6 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { let desired_targets = desired_targets.clone(); let client = client.clone(); let crates_io_api_client = crates_io_api_client.clone(); - let cli_overrides = cli_overrides.clone(); let install_path = install_path.clone(); tokio::spawn(async move { @@ -325,7 +324,6 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { opts.clone(), crate_name, desired_targets.clone(), - cli_overrides, temp_dir_path, install_path, client, From 6a95bb07e0781fa84fe2ef314f9a641b0b90f837 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 15:05:12 +1000 Subject: [PATCH 08/12] Add field `desired_targets` to `binstall::Options` Signed-off-by: Jiahao XU --- src/binstall.rs | 3 ++- src/binstall/install.rs | 3 +-- src/binstall/resolve.rs | 3 +-- src/main.rs | 8 ++------ 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/binstall.rs b/src/binstall.rs index 57a83e13..02e608c8 100644 --- a/src/binstall.rs +++ b/src/binstall.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use crate::PkgOverride; +use crate::{DesiredTargets, PkgOverride}; mod resolve; pub use resolve::*; @@ -14,4 +14,5 @@ pub struct Options { pub version: Option, pub manifest_path: Option, pub cli_overrides: PkgOverride, + pub desired_targets: DesiredTargets, } diff --git a/src/binstall/install.rs b/src/binstall/install.rs index 6c1830ea..c1b78fc3 100644 --- a/src/binstall/install.rs +++ b/src/binstall/install.rs @@ -11,7 +11,6 @@ use crate::{bins, fetchers::Fetcher, *}; pub async fn install( resolution: Resolution, opts: Arc, - desired_targets: DesiredTargets, jobserver_client: LazyJobserverClient, ) -> Result<()> { match resolution { @@ -32,7 +31,7 @@ pub async fn install( install_from_package(fetcher, opts, cvs, version, bin_path, bin_files).await } Resolution::InstallFromSource { package } => { - let desired_targets = desired_targets.get().await; + let desired_targets = opts.desired_targets.get().await; let target = desired_targets .first() .ok_or_else(|| miette!("No viable targets found, try with `--targets`"))?; diff --git a/src/binstall/resolve.rs b/src/binstall/resolve.rs index 9e77490e..f2b4f8c6 100644 --- a/src/binstall/resolve.rs +++ b/src/binstall/resolve.rs @@ -75,7 +75,6 @@ impl Resolution { pub async fn resolve( opts: Arc, crate_name: CrateName, - desired_targets: DesiredTargets, temp_dir: Arc, install_path: Arc, client: Client, @@ -123,7 +122,7 @@ pub async fn resolve( let mut fetchers = MultiFetcher::default(); - let desired_targets = desired_targets.get().await; + let desired_targets = opts.desired_targets.get().await; for target in desired_targets { debug!("Building metadata for target: {target}"); diff --git a/src/main.rs b/src/main.rs index 4f90b51c..31daa6fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -266,6 +266,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { version: opts.version.take(), manifest_path: opts.manifest_path.take(), cli_overrides, + desired_targets, }); let tasks: Vec<_> = if !opts.dry_run && !opts.no_confirm { @@ -276,7 +277,6 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { tokio::spawn(binstall::resolve( binstall_opts.clone(), crate_name, - desired_targets.clone(), temp_dir_path.clone(), install_path.clone(), client.clone(), @@ -300,7 +300,6 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { tokio::spawn(binstall::install( resolution, binstall_opts.clone(), - desired_targets.clone(), jobserver_client.clone(), )) }) @@ -312,9 +311,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { .map(|crate_name| { let opts = binstall_opts.clone(); let temp_dir_path = temp_dir_path.clone(); - let desired_target = desired_targets.clone(); let jobserver_client = jobserver_client.clone(); - let desired_targets = desired_targets.clone(); let client = client.clone(); let crates_io_api_client = crates_io_api_client.clone(); let install_path = install_path.clone(); @@ -323,7 +320,6 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { let resolution = binstall::resolve( opts.clone(), crate_name, - desired_targets.clone(), temp_dir_path, install_path, client, @@ -331,7 +327,7 @@ async fn entry(jobserver_client: LazyJobserverClient) -> Result<()> { ) .await?; - binstall::install(resolution, opts, desired_target, jobserver_client).await + binstall::install(resolution, opts, jobserver_client).await }) }) .collect() From eda7b9445a1c7d7a1797574f7696c39af1dba63b Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 15:06:00 +1000 Subject: [PATCH 09/12] Rm unnecessary `Arc` inside` DesiredTargetsInner` Since `DesiredTargets` is now stored in `binstall::Options`, which itself is wrapped in an `Arc`, `DesiredTargetsInner::Initialized` no longer needs an `Arc` for O(1) `clone`. Signed-off-by: Jiahao XU --- src/target.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target.rs b/src/target.rs index c6bf8dd9..fdca606f 100644 --- a/src/target.rs +++ b/src/target.rs @@ -11,7 +11,7 @@ pub const TARGET: &str = env!("TARGET"); #[derive(Debug, Clone)] enum DesiredTargetsInner { AutoDetect(Arc>>), - Initialized(Arc>), + Initialized(Vec), } #[derive(Debug, Clone)] @@ -19,7 +19,7 @@ pub struct DesiredTargets(DesiredTargetsInner); impl DesiredTargets { fn initialized(targets: Vec) -> Self { - Self(DesiredTargetsInner::Initialized(Arc::new(targets))) + Self(DesiredTargetsInner::Initialized(targets)) } fn auto_detect() -> Self { From b2d09e2b139d7157e5e6aedba1fbf149e64f192d Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 15:07:09 +1000 Subject: [PATCH 10/12] Rm unused `derive(Clone)` for `DesiredTargets` Signed-off-by: Jiahao XU --- src/target.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/target.rs b/src/target.rs index fdca606f..8dfb75b6 100644 --- a/src/target.rs +++ b/src/target.rs @@ -8,13 +8,13 @@ use tokio::sync::OnceCell; /// Compiled target triple, used as default for binary fetching pub const TARGET: &str = env!("TARGET"); -#[derive(Debug, Clone)] +#[derive(Debug)] enum DesiredTargetsInner { AutoDetect(Arc>>), Initialized(Vec), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct DesiredTargets(DesiredTargetsInner); impl DesiredTargets { From e308b275d517739e7085055d55b16210cbbf59c6 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 19:18:43 +1000 Subject: [PATCH 11/12] Optimize and generalize `find_version` - Rm the process of collecting into `BTreeMap` in `find_version`. - Accept any type that implements trait `Version` Signed-off-by: Jiahao XU --- src/drivers/crates_io.rs | 18 ++--------- src/drivers/version.rs | 64 ++++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/drivers/crates_io.rs b/src/drivers/crates_io.rs index b7e96086..5d297d40 100644 --- a/src/drivers/crates_io.rs +++ b/src/drivers/crates_io.rs @@ -34,22 +34,8 @@ pub async fn fetch_crate_cratesio( })?; // Locate matching version - let version_iter = - base_info - .versions - .iter() - .filter_map(|v| if !v.yanked { Some(&v.num) } else { None }); - let version_name = find_version(version_req, version_iter)?; - - // Fetch information for the filtered version - let version = base_info - .versions - .iter() - .find(|v| v.num == version_name.to_string()) - .ok_or_else(|| BinstallError::VersionUnavailable { - crate_name: name.into(), - v: version_name.clone(), - })?; + let version_iter = base_info.versions.iter().filter(|v| !v.yanked); + let (version, version_name) = find_version(version_req, version_iter)?; debug!("Found information for crate version: '{}'", version.num); diff --git a/src/drivers/version.rs b/src/drivers/version.rs index 7d5f4a74..6ef364d5 100644 --- a/src/drivers/version.rs +++ b/src/drivers/version.rs @@ -1,48 +1,56 @@ -use std::collections::BTreeSet; - use log::debug; -use semver::{Version, VersionReq}; +use semver::VersionReq; use crate::BinstallError; -pub(super) fn find_version<'a, V: Iterator>( +pub(super) trait Version { + /// Return `None` on error. + fn get_version(&self) -> Option; +} + +impl Version for &T { + fn get_version(&self) -> Option { + (*self).get_version() + } +} + +impl Version for crates_io_api::Version { + fn get_version(&self) -> Option { + // Remove leading `v` for git tags + let ver_str = match self.num.strip_prefix('s') { + Some(v) => v, + None => &self.num, + }; + + // Parse out version + semver::Version::parse(ver_str).ok() + } +} + +pub(super) fn find_version>( requirement: &str, - version_iter: V, -) -> Result { + version_iter: VersionIter, +) -> Result<(Item, semver::Version), BinstallError> { // Parse version requirement let version_req = VersionReq::parse(requirement).map_err(|err| BinstallError::VersionReq { req: requirement.into(), err, })?; - // Filter for matching versions - let filtered: BTreeSet<_> = version_iter - .filter_map(|v| { - // Remove leading `v` for git tags - let ver_str = match v.strip_prefix('s') { - Some(v) => v, - None => v, - }; - - // Parse out version - let ver = Version::parse(ver_str).ok()?; - debug!("Version: {:?}", ver); + version_iter + // Filter for matching versions + .filter_map(|item| { + let ver = item.get_version()?; // Filter by version match if version_req.matches(&ver) { - Some(ver) + debug!("Version: {:?}", ver); + Some((item, ver)) } else { None } }) - .collect(); - - debug!("Filtered: {:?}", filtered); - - // Return highest version - filtered - .iter() - .max() - .cloned() + // Return highest version + .max_by_key(|(_item, ver)| ver.clone()) .ok_or(BinstallError::VersionMismatch { req: version_req }) } From 4f0f01b75c10a755fa238145a5984b745eff981d Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 21 Jul 2022 19:20:21 +1000 Subject: [PATCH 12/12] Fix typo in `impl Version for crates_io_api::Version` Signed-off-by: Jiahao XU --- src/drivers/version.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/version.rs b/src/drivers/version.rs index 6ef364d5..35e11299 100644 --- a/src/drivers/version.rs +++ b/src/drivers/version.rs @@ -17,7 +17,7 @@ impl Version for &T { impl Version for crates_io_api::Version { fn get_version(&self) -> Option { // Remove leading `v` for git tags - let ver_str = match self.num.strip_prefix('s') { + let ver_str = match self.num.strip_prefix('v') { Some(v) => v, None => &self.num, };