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; use std::path::Path;
pub use gh_release::*; pub use gh_release::*;
pub use quickinstall::*;
use crate::PkgMeta; use crate::PkgMeta;
mod gh_release; mod gh_release;
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<Self, anyhow::Error> async fn new(data: &Data) -> Result<Box<Self>, anyhow::Error> where Self: Sized;
where
Self: std::marker::Sized;
/// Fetch a package /// Fetch a package
async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error>; async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error>;
@ -21,10 +21,32 @@ pub trait Fetcher {
} }
/// Data required to fetch a package /// Data required to fetch a package
#[derive(Debug)]
pub struct Data { pub struct Data {
pub name: String, pub name: String,
pub target: String, pub target: String,
pub version: String, pub version: String,
pub repo: Option<String>, pub repo: Option<String>,
pub meta: PkgMeta, 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 std::path::Path;
use log::{debug, info}; use log::{debug, info};
use reqwest::Method;
use serde::Serialize; use serde::Serialize;
use crate::{download, head, Template}; use crate::{download, remote_exists, Template};
use super::Data; use super::Data;
pub struct GhRelease { pub struct GhRelease {
@ -12,7 +13,7 @@ pub struct GhRelease {
#[async_trait::async_trait] #[async_trait::async_trait]
impl super::Fetcher for GhRelease { 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 // Generate context for URL interpolation
let ctx = Context { let ctx = Context {
name: &data.name, name: &data.name,
@ -23,12 +24,12 @@ impl super::Fetcher for GhRelease {
}; };
debug!("Using context: {:?}", ctx); 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> { async fn check(&self) -> Result<bool, anyhow::Error> {
info!("Checking for package at: '{}'", self.url); 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> { 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) Ok(manifest)
} }
pub async fn head(url: &str) -> Result<bool, anyhow::Error> { pub async fn remote_exists(url: &str, method: reqwest::Method) -> Result<bool, anyhow::Error> {
let req = reqwest::Client::new().head(url).send().await?; let req = reqwest::Client::new().request(method, url).send().await?;
Ok(req.status().is_success()) Ok(req.status().is_success())
} }

View file

@ -7,7 +7,7 @@ use structopt::StructOpt;
use tempdir::TempDir; use tempdir::TempDir;
use cargo_binstall::{*, fetchers::{GhRelease, Data, Fetcher}, bins}; use cargo_binstall::{*, fetchers::{GhRelease, Data, Fetcher, QuickInstall, MultiFetcher}, bins};
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
@ -127,16 +127,19 @@ async fn main() -> Result<(), anyhow::Error> {
repo: package.repository.clone(), repo: package.repository.clone(),
meta: meta.clone(), meta: meta.clone(),
}; };
// Try github releases // Try github releases, then quickinstall
let gh = GhRelease::new(&fetcher_data).await?; let mut fetchers = MultiFetcher::default();
if !gh.check().await? { fetchers.add(GhRelease::new(&fetcher_data).await?);
error!("No file found in github releases, cannot proceed"); fetchers.add(QuickInstall::new(&fetcher_data).await?);
return Err(anyhow::anyhow!("No viable remote package found"));
} 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 // Download package
gh.fetch(&pkg_path).await?; fetcher.fetch(&pkg_path).await?;
#[cfg(incomplete)] #[cfg(incomplete)]
{ {