use std::sync::Arc;
use tokio::process::Command;
use tokio::sync::Semaphore;
use crate::locale::detection::detect_language;
use crate::models::{BinstallKind, PackageInfo};
const MAX_CONCURRENT_PROBES: usize = 8;
pub fn parse_dry_run(output: &str) -> BinstallKind {
if output.contains("will be installed from source") {
BinstallKind::SourceBuild
} else if output.contains("has been downloaded from") {
BinstallKind::Prebuilt
} else {
BinstallKind::Unknown
}
}
pub async fn probe(package_name: &str, version: &str) -> BinstallKind {
let spec = format!("{package_name}@{version}");
match Command::new("cargo")
.args(["binstall", "--dry-run", "--no-confirm", &spec])
.output()
.await
{
Ok(out) => {
let mut text = String::from_utf8_lossy(&out.stderr).into_owned();
text.push_str(&String::from_utf8_lossy(&out.stdout));
parse_dry_run(&text)
}
Err(_) => BinstallKind::Unknown,
}
}
pub async fn annotate_updates(packages: &mut [PackageInfo]) {
let targets: Vec<(usize, String, String)> = packages
.iter()
.enumerate()
.filter(|(_, p)| p.source.is_crates() && p.has_update())
.filter_map(|(i, p)| p.latest_version.clone().map(|v| (i, p.name.clone(), v)))
.collect();
if targets.is_empty() {
return;
}
crate::display::status(
"Checking",
&detect_language()
.get_text("checking_binstall")
.replace("{}", &targets.len().to_string()),
);
let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT_PROBES));
let mut handles = Vec::new();
for (index, name, version) in targets {
let sem = semaphore.clone();
handles.push(tokio::spawn(async move {
let _permit = sem.acquire_owned().await.ok();
(index, probe(&name, &version).await)
}));
}
for handle in handles {
if let Ok((index, kind)) = handle.await {
packages[index].binstall_kind = Some(kind);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prebuilt_from_github_release() {
let out = " INFO resolve: Resolving package: 'cargo-deny@=0.19.7'\n\
WARN The package cargo-deny v0.19.7 (aarch64-apple-darwin) has been downloaded from github.com\n\
INFO This will install the following binaries:\n";
assert_eq!(parse_dry_run(out), BinstallKind::Prebuilt);
}
#[test]
fn prebuilt_from_quickinstall() {
let out = " WARN The package cargo-asm v0.1.16 (aarch64-apple-darwin) has been \
downloaded from third-party source QuickInstall\n\
INFO This will install the following binaries:\n";
assert_eq!(parse_dry_run(out), BinstallKind::Prebuilt);
}
#[test]
fn source_build_when_no_prebuilt() {
let out = " INFO resolve: Resolving package: 'cargo-count'\n\
WARN The package cargo-count v0.2.4 will be installed from source (with cargo)\n\
INFO Dry-run: running `cargo install cargo-count --version 0.2.4`\n";
assert_eq!(parse_dry_run(out), BinstallKind::SourceBuild);
}
#[test]
fn unknown_when_not_found_or_garbage() {
let not_found = " ERROR Fatal error:\n × cargo-no-such-crate is not found\n";
assert_eq!(parse_dry_run(not_found), BinstallKind::Unknown);
assert_eq!(parse_dry_run(""), BinstallKind::Unknown);
}
}