diff --git a/crates/binstalk/src/fetchers.rs b/crates/binstalk/src/fetchers.rs index 1b9d324c..57f5f662 100644 --- a/crates/binstalk/src/fetchers.rs +++ b/crates/binstalk/src/fetchers.rs @@ -3,6 +3,8 @@ use std::{path::Path, sync::Arc}; use compact_str::CompactString; pub use gh_crate_meta::*; pub use quickinstall::*; +use tokio::sync::OnceCell; +use url::Url; use crate::{ errors::BinstallError, @@ -60,9 +62,37 @@ pub trait Fetcher: Send + Sync { /// Data required to fetch a package #[derive(Clone, Debug)] pub struct Data { - pub name: CompactString, - pub version: CompactString, - pub repo: Option, + name: CompactString, + version: CompactString, + repo: Option, + repo_final_url: OnceCell>, +} + +impl Data { + pub fn new(name: CompactString, version: CompactString, repo: Option) -> Self { + Self { + name, + version, + repo, + repo_final_url: OnceCell::new(), + } + } + + async fn resolve_final_repo_url(&self, client: &Client) -> Result<&Option, BinstallError> { + self.repo_final_url + .get_or_try_init(move || { + Box::pin(async move { + if let Some(repo) = self.repo.as_deref() { + Ok(Some( + client.get_redirected_final_url(Url::parse(repo)?).await?, + )) + } else { + Ok(None) + } + }) + }) + .await + } } /// Target specific data required to fetch a package diff --git a/crates/binstalk/src/fetchers/gh_crate_meta.rs b/crates/binstalk/src/fetchers/gh_crate_meta.rs index 14478a68..2940afc2 100644 --- a/crates/binstalk/src/fetchers/gh_crate_meta.rs +++ b/crates/binstalk/src/fetchers/gh_crate_meta.rs @@ -92,11 +92,7 @@ impl super::Fetcher for GhCrateMeta { fn find(self: Arc) -> AutoAbortJoinHandle> { AutoAbortJoinHandle::spawn(async move { - let repo = if let Some(repo) = self.data.repo.as_deref() { - Some(Box::pin(self.client.get_redirected_final_url(Url::parse(repo)?)).await?) - } else { - None - }; + let repo = self.data.resolve_final_repo_url(&self.client).await?; let mut pkg_fmt = self.target_data.meta.pkg_fmt; @@ -156,8 +152,7 @@ impl super::Fetcher for GhCrateMeta { }; // Convert Option to Option to reduce size of future. - let repo = repo.map(String::from); - let repo = repo.as_deref().map(|u| u.trim_end_matches('/')); + let repo = repo.as_ref().map(|u| u.as_str().trim_end_matches('/')); // Use reference to self to fix error of closure // launch_baseline_find_tasks which moves `this` @@ -337,11 +332,11 @@ mod test { #[test] fn defaults() { - let data = Data { - name: "cargo-binstall".to_compact_string(), - version: "1.2.3".to_compact_string(), - repo: Some("https://github.com/ryankurte/cargo-binstall".to_string()), - }; + let data = Data::new( + "cargo-binstall".to_compact_string(), + "1.2.3".to_compact_string(), + Some("https://github.com/ryankurte/cargo-binstall".to_string()), + ); let ctx = Context::from_data(&data, "x86_64-unknown-linux-gnu", ".tgz"); assert_eq!( @@ -354,11 +349,11 @@ mod test { #[should_panic] fn no_repo() { let meta = PkgMeta::default(); - let data = Data { - name: "cargo-binstall".to_compact_string(), - version: "1.2.3".to_compact_string(), - repo: None, - }; + let data = Data::new( + "cargo-binstall".to_compact_string(), + "1.2.3".to_compact_string(), + None, + ); let ctx = Context::from_data(&data, "x86_64-unknown-linux-gnu", ".tgz"); ctx.render_url(meta.pkg_url.as_deref().unwrap()).unwrap(); @@ -371,11 +366,11 @@ mod test { ..Default::default() }; - let data = Data { - name: "cargo-binstall".to_compact_string(), - version: "1.2.3".to_compact_string(), - repo: None, - }; + let data = Data::new( + "cargo-binstall".to_compact_string(), + "1.2.3".to_compact_string(), + None, + ); let ctx = Context::from_data(&data, "x86_64-unknown-linux-gnu", ".tgz"); assert_eq!( @@ -393,11 +388,11 @@ mod test { ..Default::default() }; - let data = Data { - name: "radio-sx128x".to_compact_string(), - version: "0.14.1-alpha.5".to_compact_string(), - repo: Some("https://github.com/rust-iot/rust-radio-sx128x".to_string()), - }; + let data = Data::new( + "radio-sx128x".to_compact_string(), + "0.14.1-alpha.5".to_compact_string(), + Some("https://github.com/rust-iot/rust-radio-sx128x".to_string()), + ); let ctx = Context::from_data(&data, "x86_64-unknown-linux-gnu", ".tgz"); assert_eq!( @@ -413,11 +408,11 @@ mod test { ..Default::default() }; - let data = Data { - name: "radio-sx128x".to_compact_string(), - version: "0.14.1-alpha.5".to_compact_string(), - repo: Some("https://github.com/rust-iot/rust-radio-sx128x".to_string()), - }; + let data = Data::new( + "radio-sx128x".to_compact_string(), + "0.14.1-alpha.5".to_compact_string(), + Some("https://github.com/rust-iot/rust-radio-sx128x".to_string()), + ); let ctx = Context::from_data(&data, "x86_64-unknown-linux-gnu", ".tgz"); assert_eq!( @@ -437,11 +432,11 @@ mod test { ..Default::default() }; - let data = Data { - name: "cargo-watch".to_compact_string(), - version: "9.0.0".to_compact_string(), - repo: Some("https://github.com/watchexec/cargo-watch".to_string()), - }; + let data = Data::new( + "cargo-watch".to_compact_string(), + "9.0.0".to_compact_string(), + Some("https://github.com/watchexec/cargo-watch".to_string()), + ); let ctx = Context::from_data(&data, "aarch64-apple-darwin", ".txz"); assert_eq!( @@ -458,11 +453,11 @@ mod test { ..Default::default() }; - let data = Data { - name: "cargo-watch".to_compact_string(), - version: "9.0.0".to_compact_string(), - repo: Some("https://github.com/watchexec/cargo-watch".to_string()), - }; + let data = Data::new( + "cargo-watch".to_compact_string(), + "9.0.0".to_compact_string(), + Some("https://github.com/watchexec/cargo-watch".to_string()), + ); let ctx = Context::from_data(&data, "aarch64-pc-windows-msvc", ".bin"); assert_eq!( diff --git a/crates/binstalk/src/ops/resolve.rs b/crates/binstalk/src/ops/resolve.rs index 4140b062..4beb5101 100644 --- a/crates/binstalk/src/ops/resolve.rs +++ b/crates/binstalk/src/ops/resolve.rs @@ -84,11 +84,11 @@ async fn resolve_inner( let mut handles: Vec<(Arc, _)> = Vec::with_capacity(desired_targets.len() * resolvers.len()); - let data = Arc::new(Data { - name: package_info.name.clone(), - version: package_info.version_str.clone(), - repo: package_info.repo.clone(), - }); + let data = Arc::new(Data::new( + package_info.name.clone(), + package_info.version_str.clone(), + package_info.repo.clone(), + )); handles.extend( desired_targets