Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize fetch_crate_cratesio for exact version #1089

Merged
merged 1 commit into from
May 25, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
126 changes: 99 additions & 27 deletions crates/binstalk/src/drivers/crates_io.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::path::PathBuf;

use cargo_toml::Manifest;
use compact_str::CompactString;
use semver::VersionReq;
use compact_str::{CompactString, ToCompactString};
use semver::{Comparator, Op as ComparatorOp, Version as SemVersion, VersionReq};
use serde::Deserialize;
use tracing::debug;

Expand All @@ -21,38 +21,68 @@ mod vfs;
mod visitor;
use visitor::ManifestVisitor;

#[derive(Deserialize)]
struct CrateInfo {
#[serde(rename = "crate")]
inner: CrateInfoInner,
}
async fn is_crate_yanked(
client: &Client,
name: &str,
version: &str,
) -> Result<bool, BinstallError> {
#[derive(Deserialize)]
struct CrateInfo {
version: Inner,
}

#[derive(Deserialize)]
struct Inner {
yanked: bool,
}

#[derive(Deserialize)]
struct CrateInfoInner {
max_stable_version: CompactString,
}
// Fetch / update index
debug!("Looking up crate information");

#[derive(Deserialize)]
struct Versions {
versions: Vec<Version>,
}
let response = client
.get(Url::parse(&format!(
"https://crates.io/api/v1/crates/{name}/{version}"
))?)
.send(true)
.await
.map_err(|err| {
BinstallError::CratesIoApi(Box::new(CratesIoApiError {
crate_name: name.into(),
err,
}))
})?;

let info: CrateInfo = response.json().await?;

#[derive(Deserialize)]
struct Version {
num: CompactString,
yanked: bool,
Ok(info.version.yanked)
}

/// Find the crate by name, get its latest stable version matches `version_req`,
/// retrieve its Cargo.toml and infer all its bins.
pub async fn fetch_crate_cratesio(
client: Client,
async fn fetch_crate_cratesio_version_matched(
client: &Client,
name: &str,
version_req: &VersionReq,
crates_io_rate_limit: &CratesIoRateLimit,
) -> Result<Manifest<Meta>, BinstallError> {
// Wait until we can make another request to crates.io
crates_io_rate_limit.tick().await;
) -> Result<CompactString, BinstallError> {
#[derive(Deserialize)]
struct CrateInfo {
#[serde(rename = "crate")]
inner: CrateInfoInner,
}

#[derive(Deserialize)]
struct CrateInfoInner {
max_stable_version: CompactString,
}

#[derive(Deserialize)]
struct Versions {
versions: Vec<Version>,
}

#[derive(Deserialize)]
struct Version {
num: CompactString,
yanked: bool,
}

// Fetch / update index
debug!("Looking up crate information");
Expand Down Expand Up @@ -106,6 +136,48 @@ pub async fn fetch_crate_cratesio(

debug!("Found information for crate version: '{version}'");

Ok(version)
}

/// Find the crate by name, get its latest stable version matches `version_req`,
/// retrieve its Cargo.toml and infer all its bins.
pub async fn fetch_crate_cratesio(
client: Client,
name: &str,
version_req: &VersionReq,
crates_io_rate_limit: &CratesIoRateLimit,
) -> Result<Manifest<Meta>, BinstallError> {
// Wait until we can make another request to crates.io
crates_io_rate_limit.tick().await;

let version = match version_req.comparators.as_slice() {
[Comparator {
op: ComparatorOp::Exact,
major,
minor: Some(minor),
patch: Some(patch),
pre,
}] => {
let version = SemVersion {
major: *major,
minor: *minor,
patch: *patch,
pre: pre.clone(),
build: Default::default(),
}
.to_compact_string();

if is_crate_yanked(&client, name, &version).await? {
return Err(BinstallError::VersionMismatch {
req: version_req.clone(),
});
}

version
}
_ => fetch_crate_cratesio_version_matched(&client, name, version_req).await?,
};

// Download crate to temporary dir (crates.io or git?)
let crate_url = format!("https://crates.io/api/v1/crates/{name}/{version}/download");

Expand Down