diff --git a/src/fetchers.rs b/src/fetchers.rs index b7b25795..b532d1e3 100644 --- a/src/fetchers.rs +++ b/src/fetchers.rs @@ -1,8 +1,10 @@ use std::path::Path; +use std::sync::Arc; pub use gh_crate_meta::*; pub use log::debug; pub use quickinstall::*; +use tokio::task::JoinHandle; use crate::{BinstallError, PkgFmt, PkgMeta}; @@ -10,9 +12,9 @@ mod gh_crate_meta; mod quickinstall; #[async_trait::async_trait] -pub trait Fetcher { +pub trait Fetcher: Send + Sync { /// Create a new fetcher from some data - async fn new(data: &Data) -> Box + async fn new(data: &Data) -> Arc where Self: Sized; @@ -44,30 +46,59 @@ pub struct Data { #[derive(Default)] pub struct MultiFetcher { - fetchers: Vec>, + fetchers: Vec>, } impl MultiFetcher { - pub fn add(&mut self, fetcher: Box) { + pub fn add(&mut self, fetcher: Arc) { self.fetchers.push(fetcher); } - pub async fn first_available(&self) -> Option<&dyn Fetcher> { - for fetcher in &self.fetchers { - let available = fetcher.check().await.unwrap_or_else(|err| { - debug!( - "Error while checking fetcher {}: {}", - fetcher.source_name(), - err - ); - false - }); + pub async fn first_available(&self) -> Option> { + let handles: Vec<_> = self + .fetchers + .iter() + .cloned() + .map(|fetcher| { + let fetcher_cloned = fetcher.clone(); - if available { - return Some(&**fetcher); + ( + AutoAbortJoinHandle(tokio::spawn(async move { fetcher.check().await })), + fetcher_cloned, + ) + }) + .collect(); + + for (mut handle, fetcher) in handles { + match (&mut handle.0).await { + Ok(Ok(true)) => return Some(fetcher), + Ok(Ok(false)) => (), + Ok(Err(err)) => { + debug!( + "Error while checking fetcher {}: {}", + fetcher.source_name(), + err + ); + } + Err(join_err) => { + debug!( + "Error while checking fetcher {}: {}", + fetcher.source_name(), + join_err + ); + } } } None } } + +#[derive(Debug)] +struct AutoAbortJoinHandle(JoinHandle>); + +impl Drop for AutoAbortJoinHandle { + fn drop(&mut self) { + self.0.abort(); + } +} diff --git a/src/fetchers/gh_crate_meta.rs b/src/fetchers/gh_crate_meta.rs index f048bb87..12bb22ab 100644 --- a/src/fetchers/gh_crate_meta.rs +++ b/src/fetchers/gh_crate_meta.rs @@ -1,4 +1,5 @@ use std::path::Path; +use std::sync::Arc; use log::{debug, info, warn}; use reqwest::Method; @@ -22,8 +23,8 @@ impl GhCrateMeta { #[async_trait::async_trait] impl super::Fetcher for GhCrateMeta { - async fn new(data: &Data) -> Box { - Box::new(Self { data: data.clone() }) + async fn new(data: &Data) -> Arc { + Arc::new(Self { data: data.clone() }) } async fn check(&self) -> Result { diff --git a/src/fetchers/quickinstall.rs b/src/fetchers/quickinstall.rs index 61871e5c..96487851 100644 --- a/src/fetchers/quickinstall.rs +++ b/src/fetchers/quickinstall.rs @@ -1,4 +1,5 @@ use std::path::Path; +use std::sync::Arc; use log::info; use reqwest::Method; @@ -17,11 +18,11 @@ pub struct QuickInstall { #[async_trait::async_trait] impl super::Fetcher for QuickInstall { - async fn new(data: &Data) -> Box { + async fn new(data: &Data) -> Arc { let crate_name = &data.name; let version = &data.version; let target = &data.target; - Box::new(Self { + Arc::new(Self { package: format!("{crate_name}-{version}-{target}"), }) } diff --git a/src/main.rs b/src/main.rs index a9479a2d..5c913dc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -234,7 +234,7 @@ async fn entry() -> Result<()> { Some(fetcher) => { install_from_package( binaries, - fetcher, + &*fetcher, install_path, meta, opts,