QuickInstall support (#94)

See this issue: https://github.com/alsuren/cargo-quickinstall/issues/27

Quick Install is a hosted repo of built crates, essentially. The approach I've taken here is
a list of strategies:

1. First, we check the crate meta or default and build the URL to the repo. Once we have
   that, we perform a `HEAD` request to the URL to see if it's available.
2. If it's not, we build the URL to the quickinstall repo, and perform a `HEAD` to there.

As soon as we've got a hit, we use that. I've built it so it's extensible with more strategies.
This could be useful for #4.

This also adds a prompt before downloading from third-party sources, and logs a short
name for a source, which is easier to glance than a full URL, and includes a quick refactor
of the install/link machinery.
This commit is contained in:
Félix Saparelli 2022-02-16 14:49:07 +13:00 committed by GitHub
parent e691255650
commit 370ae05620
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 600 additions and 209 deletions

View file

@ -0,0 +1,74 @@
use std::path::Path;
use log::{debug, info};
use reqwest::Method;
use serde::Serialize;
use url::Url;
use super::Data;
use crate::{download, remote_exists, PkgFmt, Template};
pub struct GhCrateMeta {
url: Url,
pkg_fmt: PkgFmt,
}
#[async_trait::async_trait]
impl super::Fetcher for GhCrateMeta {
async fn new(data: &Data) -> Result<Box<Self>, anyhow::Error> {
// Generate context for URL interpolation
let ctx = Context {
name: &data.name,
repo: data.repo.as_ref().map(|s| &s[..]),
target: &data.target,
version: &data.version,
format: data.meta.pkg_fmt.to_string(),
};
debug!("Using context: {:?}", ctx);
Ok(Box::new(Self {
url: Url::parse(&ctx.render(&data.meta.pkg_url)?)?,
pkg_fmt: data.meta.pkg_fmt,
}))
}
async fn check(&self) -> Result<bool, anyhow::Error> {
info!("Checking for package at: '{}'", self.url);
remote_exists(self.url.as_str(), Method::HEAD).await
}
async fn fetch(&self, dst: &Path) -> Result<(), anyhow::Error> {
info!("Downloading package from: '{}'", self.url);
download(self.url.as_str(), dst).await
}
fn pkg_fmt(&self) -> PkgFmt {
self.pkg_fmt
}
fn source_name(&self) -> String {
if let Some(domain) = self.url.domain() {
domain.to_string()
} else if let Some(host) = self.url.host_str() {
host.to_string()
} else {
self.url.to_string()
}
}
fn is_third_party(&self) -> bool {
false
}
}
/// Template for constructing download paths
#[derive(Clone, Debug, Serialize)]
struct Context<'c> {
pub name: &'c str,
pub repo: Option<&'c str>,
pub target: &'c str,
pub version: &'c str,
pub format: String,
}
impl<'c> Template for Context<'c> {}