Make fetcher creating infallible (#118)

Fixes #109
This commit is contained in:
Félix Saparelli 2022-04-29 09:24:46 +12:00 committed by GitHub
parent 62ec23e6f4
commit 1757dc5344
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 33 deletions

View file

@ -1,4 +1,4 @@
use std::path::{PathBuf, Path}; use std::path::{Path, PathBuf};
use cargo_toml::Product; use cargo_toml::Product;
use log::debug; use log::debug;
@ -118,9 +118,13 @@ impl BinFile {
fn link_dest(&self) -> &Path { fn link_dest(&self) -> &Path {
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
{ Path::new(self.dest.file_name().unwrap()) } {
Path::new(self.dest.file_name().unwrap())
}
#[cfg(target_family = "windows")] #[cfg(target_family = "windows")]
{ &self.dest } {
&self.dest
}
} }
} }

View file

@ -1,6 +1,7 @@
use std::path::Path; use std::path::Path;
pub use gh_crate_meta::*; pub use gh_crate_meta::*;
pub use log::debug;
pub use quickinstall::*; pub use quickinstall::*;
use crate::{PkgFmt, PkgMeta}; use crate::{PkgFmt, PkgMeta};
@ -11,7 +12,7 @@ mod quickinstall;
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait Fetcher { pub trait Fetcher {
/// Create a new fetcher from some data /// Create a new fetcher from some data
async fn new(data: &Data) -> Result<Box<Self>, anyhow::Error> async fn new(data: &Data) -> Box<Self>
where where
Self: Sized; Self: Sized;
@ -32,7 +33,7 @@ pub trait Fetcher {
} }
/// Data required to fetch a package /// Data required to fetch a package
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct Data { pub struct Data {
pub name: String, pub name: String,
pub target: String, pub target: String,
@ -53,7 +54,14 @@ impl MultiFetcher {
pub async fn first_available(&self) -> Option<&dyn Fetcher> { pub async fn first_available(&self) -> Option<&dyn Fetcher> {
for fetcher in &self.fetchers { for fetcher in &self.fetchers {
if fetcher.check().await.unwrap_or(false) { if fetcher.check().await.unwrap_or_else(|err| {
debug!(
"Error while checking fetcher {}: {}",
fetcher.source_name(),
err
);
false
}) {
return Some(&**fetcher); return Some(&**fetcher);
} }
} }

View file

@ -9,44 +9,51 @@ use super::Data;
use crate::{download, remote_exists, PkgFmt, Template}; use crate::{download, remote_exists, PkgFmt, Template};
pub struct GhCrateMeta { pub struct GhCrateMeta {
url: Url, data: Data,
pkg_fmt: PkgFmt, }
impl GhCrateMeta {
fn url(&self) -> Result<Url, anyhow::Error> {
let ctx = Context::from_data(&self.data);
debug!("Using context: {:?}", ctx);
Ok(ctx.render_url(&self.data.meta.pkg_url)?)
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl super::Fetcher for GhCrateMeta { impl super::Fetcher for GhCrateMeta {
async fn new(data: &Data) -> Result<Box<Self>, anyhow::Error> { async fn new(data: &Data) -> Box<Self> {
let ctx = Context::from_data(data); Box::new(Self { data: data.clone() })
debug!("Using context: {:?}", ctx);
Ok(Box::new(Self {
url: ctx.render_url(&data.meta.pkg_url)?,
pkg_fmt: data.meta.pkg_fmt,
}))
} }
async fn check(&self) -> Result<bool, anyhow::Error> { async fn check(&self) -> Result<bool, anyhow::Error> {
info!("Checking for package at: '{}'", self.url); let url = self.url()?;
remote_exists(self.url.as_str(), Method::HEAD).await info!("Checking for package at: '{url}'");
remote_exists(url.as_str(), Method::HEAD).await
} }
async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error> { async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error> {
info!("Downloading package from: '{}'", self.url); let url = self.url()?;
download(self.url.as_str(), dst).await info!("Downloading package from: '{url}'");
download(url.as_str(), dst).await
} }
fn pkg_fmt(&self) -> PkgFmt { fn pkg_fmt(&self) -> PkgFmt {
self.pkg_fmt self.data.meta.pkg_fmt
} }
fn source_name(&self) -> String { fn source_name(&self) -> String {
if let Some(domain) = self.url.domain() { self.url()
domain.to_string() .map(|url| {
} else if let Some(host) = self.url.host_str() { if let Some(domain) = url.domain() {
host.to_string() domain.to_string()
} else { } else if let Some(host) = url.host_str() {
self.url.to_string() host.to_string()
} } else {
url.to_string()
}
})
.unwrap_or_else(|_| "invalid url template".to_string())
} }
fn is_third_party(&self) -> bool { fn is_third_party(&self) -> bool {

View file

@ -16,13 +16,13 @@ pub struct QuickInstall {
#[async_trait::async_trait] #[async_trait::async_trait]
impl super::Fetcher for QuickInstall { impl super::Fetcher for QuickInstall {
async fn new(data: &Data) -> Result<Box<Self>, anyhow::Error> { async fn new(data: &Data) -> Box<Self> {
let crate_name = &data.name; let crate_name = &data.name;
let version = &data.version; let version = &data.version;
let target = &data.target; let target = &data.target;
Ok(Box::new(Self { Box::new(Self {
package: format!("{crate_name}-{version}-{target}"), package: format!("{crate_name}-{version}-{target}"),
})) })
} }
async fn check(&self) -> Result<bool, anyhow::Error> { async fn check(&self) -> Result<bool, anyhow::Error> {

View file

@ -143,8 +143,8 @@ async fn main() -> Result<(), anyhow::Error> {
// Try github releases, then quickinstall // Try github releases, then quickinstall
let mut fetchers = MultiFetcher::default(); let mut fetchers = MultiFetcher::default();
fetchers.add(GhCrateMeta::new(&fetcher_data).await?); fetchers.add(GhCrateMeta::new(&fetcher_data).await);
fetchers.add(QuickInstall::new(&fetcher_data).await?); fetchers.add(QuickInstall::new(&fetcher_data).await);
let fetcher = fetchers.first_available().await.ok_or_else(|| { let fetcher = fetchers.first_available().await.ok_or_else(|| {
error!("File does not exist remotely, cannot proceed"); error!("File does not exist remotely, cannot proceed");