fix version matching, now works with semver

This commit is contained in:
ryan 2020-12-31 15:32:58 +13:00
parent a6c70b41e2
commit ef6a3d0ef7
4 changed files with 210 additions and 45 deletions

View file

@ -2,45 +2,90 @@
use std::time::Duration;
use std::path::{Path, PathBuf};
use log::{debug, error};
use log::{debug};
use anyhow::{Context, anyhow};
use semver::{Version, VersionReq};
use crates_io_api::AsyncClient;
use crate::PkgFmt;
use crate::helpers::*;
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,
};
// Parse out version
let ver = match Version::parse(ver_str) {
Ok(sv) => sv,
Err(_) => return false,
};
debug!("Version: {:?}", ver);
// Filter by version match
version_req.matches(&ver)
}).collect();
// Sort by highest matching version
filtered.sort_by(|a, b| {
let a = Version::parse(a).unwrap();
let b = Version::parse(b).unwrap();
b.partial_cmp(&a).unwrap()
});
debug!("Filtered: {:?}", filtered);
// Return highest version
match filtered.get(0) {
Some(v) => Ok(v.to_string()),
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: Option<&str>, temp_dir: &Path) -> Result<PathBuf, anyhow::Error> {
// Build crates.io api client and fetch info
let api_client = AsyncClient::new("cargo-binstall (https://github.com/ryankurte/cargo-binstall)", Duration::from_millis(100))?;
pub async fn fetch_crate_cratesio(name: &str, version_req: &str, temp_dir: &Path) -> Result<PathBuf, anyhow::Error> {
debug!("Fetching information for crate: '{}'", name);
// Fetch / update index
debug!("Updating crates.io index");
let index = crates_index::Index::new_cargo_default();
index.retrieve_or_update()?;
// Fetch overall crate info
let info = match api_client.get_crate(name.as_ref()).await {
Ok(i) => i,
Err(e) => {
error!("Error fetching information for crate {}: {}", name, e);
return Err(e.into())
// Lookup crate in index
debug!("Looking up crate information");
let base_info = match index.crate_(name) {
Some(i) => i,
None => {
return Err(anyhow::anyhow!("Error fetching information for crate {}", name));
}
};
// Use specified or latest version
let version_num = match version {
Some(v) => v.to_string(),
None => info.crate_data.max_version,
};
// Locate matching 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))?;
// Fetch crates.io information for the specified version
// Note it is not viable to use a semver match here as crates.io
// appears to elide alpha and yanked versions in the generic response...
let versions = info.versions.clone();
let version = match versions.iter().find(|v| v.num == version_num) {
// Fetch online crate information
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 => {
error!("No crates.io information found for crate: '{}' version: '{}'",
name, version_num);
return Err(anyhow::anyhow!("No crate information found"));
return Err(anyhow::anyhow!("No information found for crate: '{}' version: '{}'",
name, version_name));
}
};
@ -58,7 +103,7 @@ pub async fn fetch_crate_cratesio(name: &str, version: Option<&str>, temp_dir: &
// Decompress downloaded tgz
debug!("Decompressing crate archive");
extract(&tgz_path, PkgFmt::Tgz, &temp_dir)?;
let crate_path = temp_dir.join(format!("{}-{}", name, version_num));
let crate_path = temp_dir.join(format!("{}-{}", name, version_name));
// Return crate directory
Ok(crate_path)