mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-05-06 04:00:02 +00:00
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:
parent
e691255650
commit
370ae05620
12 changed files with 600 additions and 209 deletions
|
@ -1,39 +1,43 @@
|
|||
|
||||
use std::time::Duration;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::{debug};
|
||||
use anyhow::{Context, anyhow};
|
||||
use anyhow::{anyhow, Context};
|
||||
use log::debug;
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
use crates_io_api::AsyncClient;
|
||||
|
||||
use crate::PkgFmt;
|
||||
use crate::helpers::*;
|
||||
use crate::PkgFmt;
|
||||
|
||||
fn find_version<'a, V: Iterator<Item=&'a str>>(requirement: &str, version_iter: V) -> Result<String, anyhow::Error> {
|
||||
fn find_version<'a, V: Iterator<Item = &'a str>>(
|
||||
requirement: &str,
|
||||
version_iter: V,
|
||||
) -> Result<String, anyhow::Error> {
|
||||
// Parse version requirement
|
||||
let version_req = VersionReq::parse(requirement)?;
|
||||
|
||||
// Filter for matching versions
|
||||
let mut filtered: Vec<_> = version_iter.filter(|v| {
|
||||
// Remove leading `v` for git tags
|
||||
let ver_str = match v.strip_prefix("s") {
|
||||
Some(v) => v,
|
||||
None => v,
|
||||
};
|
||||
let mut filtered: Vec<_> = version_iter
|
||||
.filter(|v| {
|
||||
// Remove leading `v` for git tags
|
||||
let ver_str = match v.strip_prefix("s") {
|
||||
Some(v) => v,
|
||||
None => v,
|
||||
};
|
||||
|
||||
// Parse out version
|
||||
let ver = match Version::parse(ver_str) {
|
||||
Ok(sv) => sv,
|
||||
Err(_) => return false,
|
||||
};
|
||||
// Parse out version
|
||||
let ver = match Version::parse(ver_str) {
|
||||
Ok(sv) => sv,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
debug!("Version: {:?}", ver);
|
||||
debug!("Version: {:?}", ver);
|
||||
|
||||
// Filter by version match
|
||||
version_req.matches(&ver)
|
||||
}).collect();
|
||||
// Filter by version match
|
||||
version_req.matches(&ver)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort by highest matching version
|
||||
filtered.sort_by(|a, b| {
|
||||
|
@ -48,13 +52,19 @@ fn find_version<'a, V: Iterator<Item=&'a str>>(requirement: &str, version_iter:
|
|||
// Return highest version
|
||||
match filtered.get(0) {
|
||||
Some(v) => Ok(v.to_string()),
|
||||
None => Err(anyhow!("No matching version for requirement: '{}'", version_req))
|
||||
None => Err(anyhow!(
|
||||
"No matching version for requirement: '{}'",
|
||||
version_req
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch a crate by name and version from crates.io
|
||||
pub async fn fetch_crate_cratesio(name: &str, version_req: &str, temp_dir: &Path) -> Result<PathBuf, anyhow::Error> {
|
||||
|
||||
pub async fn fetch_crate_cratesio(
|
||||
name: &str,
|
||||
version_req: &str,
|
||||
temp_dir: &Path,
|
||||
) -> Result<PathBuf, anyhow::Error> {
|
||||
// Fetch / update index
|
||||
debug!("Updating crates.io index");
|
||||
let mut index = crates_index::Index::new_cargo_default()?;
|
||||
|
@ -65,37 +75,48 @@ pub async fn fetch_crate_cratesio(name: &str, version_req: &str, temp_dir: &Path
|
|||
let base_info = match index.crate_(name) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(anyhow::anyhow!("Error fetching information for crate {}", name));
|
||||
return Err(anyhow::anyhow!(
|
||||
"Error fetching information for crate {}",
|
||||
name
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Locate matching version
|
||||
let version_iter = base_info.versions().iter().map(|v| v.version() );
|
||||
let version_iter = base_info.versions().iter().map(|v| v.version());
|
||||
let version_name = find_version(version_req, version_iter)?;
|
||||
|
||||
|
||||
// Build crates.io api client
|
||||
let api_client = AsyncClient::new("cargo-binstall (https://github.com/ryankurte/cargo-binstall)", Duration::from_millis(100))?;
|
||||
let api_client = AsyncClient::new(
|
||||
"cargo-binstall (https://github.com/ryankurte/cargo-binstall)",
|
||||
Duration::from_millis(100),
|
||||
)?;
|
||||
|
||||
// Fetch online crate information
|
||||
let crate_info = api_client.get_crate(name.as_ref()).await
|
||||
let crate_info = api_client
|
||||
.get_crate(name.as_ref())
|
||||
.await
|
||||
.context("Error fetching crate information")?;
|
||||
|
||||
// Fetch information for the filtered version
|
||||
let version = match crate_info.versions.iter().find(|v| v.num == version_name) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(anyhow::anyhow!("No information found for crate: '{}' version: '{}'",
|
||||
name, version_name));
|
||||
return Err(anyhow::anyhow!(
|
||||
"No information found for crate: '{}' version: '{}'",
|
||||
name,
|
||||
version_name
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
debug!("Found information for crate version: '{}'", version.num);
|
||||
|
||||
|
||||
// Download crate to temporary dir (crates.io or git?)
|
||||
let crate_url = format!("https://crates.io/{}", version.dl_path);
|
||||
let tgz_path = temp_dir.join(format!("{}.tgz", name));
|
||||
|
||||
debug!("Fetching crate from: {}", crate_url);
|
||||
debug!("Fetching crate from: {}", crate_url);
|
||||
|
||||
// Download crate
|
||||
download(&crate_url, &tgz_path).await?;
|
||||
|
@ -111,8 +132,10 @@ pub async fn fetch_crate_cratesio(name: &str, version_req: &str, temp_dir: &Path
|
|||
|
||||
/// Fetch a crate by name and version from github
|
||||
/// TODO: implement this
|
||||
pub async fn fetch_crate_gh_releases(_name: &str, _version: Option<&str>, _temp_dir: &Path) -> Result<PathBuf, anyhow::Error> {
|
||||
|
||||
pub async fn fetch_crate_gh_releases(
|
||||
_name: &str,
|
||||
_version: Option<&str>,
|
||||
_temp_dir: &Path,
|
||||
) -> Result<PathBuf, anyhow::Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue