diff --git a/src/fetchers.rs b/src/fetchers.rs index 85a29a51..288340cb 100644 --- a/src/fetchers.rs +++ b/src/fetchers.rs @@ -1,17 +1,17 @@ use std::path::Path; pub use gh_release::*; +pub use quickinstall::*; use crate::PkgMeta; mod gh_release; +mod quickinstall; #[async_trait::async_trait] pub trait Fetcher { /// Create a new fetcher from some data - async fn new(data: &Data) -> Result - where - Self: std::marker::Sized; + async fn new(data: &Data) -> Result, anyhow::Error> where Self: Sized; /// Fetch a package async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error>; @@ -21,10 +21,32 @@ pub trait Fetcher { } /// Data required to fetch a package +#[derive(Debug)] pub struct Data { pub name: String, pub target: String, pub version: String, pub repo: Option, pub meta: PkgMeta, +} + +#[derive(Default)] +pub struct MultiFetcher { + fetchers: Vec>, +} + +impl MultiFetcher { + pub fn add(&mut self, fetcher: Box) { + self.fetchers.push(fetcher); + } + + pub async fn first_available(&self) -> Option<&dyn Fetcher> { + for fetcher in &self.fetchers { + if fetcher.check().await.unwrap_or(false) { + return Some(&**fetcher); + } + } + + None + } } \ No newline at end of file diff --git a/src/fetchers/gh_release.rs b/src/fetchers/gh_release.rs index 17bf4a79..4b0cf9b0 100644 --- a/src/fetchers/gh_release.rs +++ b/src/fetchers/gh_release.rs @@ -1,9 +1,10 @@ use std::path::Path; use log::{debug, info}; +use reqwest::Method; use serde::Serialize; -use crate::{download, head, Template}; +use crate::{download, remote_exists, Template}; use super::Data; pub struct GhRelease { @@ -12,7 +13,7 @@ pub struct GhRelease { #[async_trait::async_trait] impl super::Fetcher for GhRelease { - async fn new(data: &Data) -> Result { + async fn new(data: &Data) -> Result, anyhow::Error> { // Generate context for URL interpolation let ctx = Context { name: &data.name, @@ -23,12 +24,12 @@ impl super::Fetcher for GhRelease { }; debug!("Using context: {:?}", ctx); - Ok(Self { url: ctx.render(&data.meta.pkg_url)? }) + Ok(Box::new(Self { url: ctx.render(&data.meta.pkg_url)? })) } async fn check(&self) -> Result { info!("Checking for package at: '{}'", self.url); - head(&self.url).await + remote_exists(&self.url, Method::OPTIONS).await } async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error> { diff --git a/src/fetchers/quickinstall.rs b/src/fetchers/quickinstall.rs new file mode 100644 index 00000000..e65e6ff6 --- /dev/null +++ b/src/fetchers/quickinstall.rs @@ -0,0 +1,31 @@ +use std::path::Path; + +use log::info; +use reqwest::Method; + +use crate::{download, remote_exists}; +use super::Data; + +pub struct QuickInstall { + url: String, +} + +#[async_trait::async_trait] +impl super::Fetcher for QuickInstall { + async fn new(data: &Data) -> Result, anyhow::Error> { + let crate_name = &data.name; + let version = &data.version; + let target = &data.target; + Ok(Box::new(Self { url: format!("https://github.com/alsuren/cargo-quickinstall/releases/download/{crate_name}-{version}-{target}/{crate_name}-{version}-{target}.tar.gz") })) + } + + async fn check(&self) -> Result { + info!("Checking for package at: '{}'", self.url); + remote_exists(&self.url, Method::OPTIONS).await + } + + async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error> { + info!("Downloading package from: '{}'", self.url); + download(&self.url, dst).await + } +} \ No newline at end of file diff --git a/src/helpers.rs b/src/helpers.rs index 216fa9fe..4ce11389 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -26,8 +26,8 @@ pub fn load_manifest_path>(manifest_path: P) -> Result Result { - let req = reqwest::Client::new().head(url).send().await?; +pub async fn remote_exists(url: &str, method: reqwest::Method) -> Result { + let req = reqwest::Client::new().request(method, url).send().await?; Ok(req.status().is_success()) } diff --git a/src/main.rs b/src/main.rs index 7b809684..0b6f72c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use structopt::StructOpt; use tempdir::TempDir; -use cargo_binstall::{*, fetchers::{GhRelease, Data, Fetcher}, bins}; +use cargo_binstall::{*, fetchers::{GhRelease, Data, Fetcher, QuickInstall, MultiFetcher}, bins}; #[derive(Debug, StructOpt)] @@ -127,16 +127,19 @@ async fn main() -> Result<(), anyhow::Error> { repo: package.repository.clone(), meta: meta.clone(), }; - - // Try github releases - let gh = GhRelease::new(&fetcher_data).await?; - if !gh.check().await? { - error!("No file found in github releases, cannot proceed"); - return Err(anyhow::anyhow!("No viable remote package found")); - } + + // Try github releases, then quickinstall + let mut fetchers = MultiFetcher::default(); + fetchers.add(GhRelease::new(&fetcher_data).await?); + fetchers.add(QuickInstall::new(&fetcher_data).await?); + + let fetcher = fetchers.first_available().await.ok_or_else(|| { + error!("File does not exist remotely, cannot proceed"); + anyhow::anyhow!("No viable remote package found") + })?; // Download package - gh.fetch(&pkg_path).await?; + fetcher.fetch(&pkg_path).await?; #[cfg(incomplete)] {