1use crate::manager::{PackageError, PackageManager, RunOutcome, Runner};
9
10pub const PREFIX: &str = "cargo:";
12
13pub struct Cargo;
16
17impl Cargo {
18 fn crate_name(spec: &str) -> &str {
20 spec.split_once('@').map_or(spec, |(name, _)| name)
21 }
22}
23
24impl PackageManager for Cargo {
25 fn name(&self) -> &'static str {
26 "cargo"
27 }
28
29 fn is_available(&self) -> bool {
30 which::which("cargo").is_ok()
31 }
32
33 fn exists(&self, runner: &dyn Runner, pkg: &str) -> Result<bool, PackageError> {
35 let RunOutcome { status, .. } = runner.run("cargo", &["info", pkg])?;
36 Ok(status == 0)
37 }
38
39 fn is_installed(&self, runner: &dyn Runner, pkg: &str) -> Result<bool, PackageError> {
42 let RunOutcome {
43 status,
44 stdout,
45 stderr,
46 } = runner.run("cargo", &["install", "--list"])?;
47 if status != 0 {
48 return Err(PackageError::ExitFailure { status, stderr });
49 }
50 let name = Self::crate_name(pkg);
51 Ok(stdout
52 .lines()
53 .filter(|line| !line.starts_with(char::is_whitespace))
54 .any(|line| line.split_whitespace().next() == Some(name)))
55 }
56
57 fn install(&self, runner: &dyn Runner, packages: &[String]) -> Result<(), PackageError> {
58 let mut args = vec!["install", "--locked"];
59 args.extend(packages.iter().map(String::as_str));
60 let RunOutcome { status, stderr, .. } = runner.run("cargo", &args)?;
61 if status != 0 {
62 return Err(PackageError::ExitFailure { status, stderr });
63 }
64 Ok(())
65 }
66}