Add quickinstall support

This commit is contained in:
Félix Saparelli 2022-02-16 00:14:37 +13:00
parent b584038d0d
commit 4e0ca0c1c3
No known key found for this signature in database
GPG key ID: B948C4BAE44FC474
5 changed files with 75 additions and 18 deletions

View file

@ -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<Self, anyhow::Error>
where
Self: std::marker::Sized;
async fn new(data: &Data) -> Result<Box<Self>, anyhow::Error> where Self: Sized;
/// Fetch a package
async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error>;
@ -21,6 +21,7 @@ pub trait Fetcher {
}
/// Data required to fetch a package
#[derive(Debug)]
pub struct Data {
pub name: String,
pub target: String,
@ -28,3 +29,24 @@ pub struct Data {
pub repo: Option<String>,
pub meta: PkgMeta,
}
#[derive(Default)]
pub struct MultiFetcher {
fetchers: Vec<Box<dyn Fetcher>>,
}
impl MultiFetcher {
pub fn add(&mut self, fetcher: Box<dyn Fetcher>) {
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
}
}

View file

@ -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<Self, anyhow::Error> {
async fn new(data: &Data) -> Result<Box<Self>, 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<bool, anyhow::Error> {
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> {

View file

@ -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<Box<Self>, 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<bool, anyhow::Error> {
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
}
}

View file

@ -26,8 +26,8 @@ pub fn load_manifest_path<P: AsRef<Path>>(manifest_path: P) -> Result<Manifest<M
Ok(manifest)
}
pub async fn head(url: &str) -> Result<bool, anyhow::Error> {
let req = reqwest::Client::new().head(url).send().await?;
pub async fn remote_exists(url: &str, method: reqwest::Method) -> Result<bool, anyhow::Error> {
let req = reqwest::Client::new().request(method, url).send().await?;
Ok(req.status().is_success())
}

View file

@ -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)]
@ -128,15 +128,18 @@ async fn main() -> Result<(), anyhow::Error> {
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)]
{