cargo_update_installed/
lib.rs

1use std::collections::BTreeMap;
2use std::io::{stderr, Write};
3use std::process::{Command, ExitStatus};
4
5extern crate tempfile;
6extern crate toml;
7
8pub fn installed_crates() -> Result<BTreeMap<String, Crate>, String> {
9    let mut cargo_list_installed = Command::new("cargo");
10    cargo_list_installed.arg("install");
11    cargo_list_installed.arg("--list");
12    let installed_output = cargo_list_installed
13        .output()
14        .map_err(|e| format!("I/O Error: {}", e))?;
15    let installed =
16        String::from_utf8(installed_output.stdout).map_err(|e| format!("UTF-8 Error: {}", e))?;
17
18    let mut crates: BTreeMap<String, Crate> = BTreeMap::new();
19    for line in installed.lines() {
20        let _crate = Crate::parse_list_output(line).map_err(|e| format!("Error: {:?}", e))?;
21        if let Some(_crate) = _crate {
22            if let Some(c) = crates.get(&_crate.name) {
23                // only consider latest version
24                // (It is possible to have two different versions of the same crate installed,
25                // for example when an old version contained an executable that is no longer
26                // present in the newer version.)
27                if c.version > _crate.version {
28                    continue;
29                }
30            }
31            crates.insert(_crate.name.clone(), _crate);
32        }
33    }
34    Ok(crates)
35}
36
37pub fn install_update(name: &str) -> Result<ExitStatus, String> {
38    let mut cargo_install_command = Command::new("cargo");
39    cargo_install_command.arg("install");
40    cargo_install_command.arg(name);
41    cargo_install_command
42        .status()
43        .map_err(|e| format!("I/O Error while running `cargo install`: {}", e))
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct Crate {
48    pub name: String,
49    pub version: String,
50    pub kind: CrateKind,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum CrateKind {
55    CratesIo,
56    /*
57        Git {
58            url: String,
59            branch: Option<String>,
60        },
61        Local,
62    */
63}
64
65impl Crate {
66    /// Parses a line from `cargo install --list`.
67    pub fn parse_list_output(line: &str) -> Result<Option<Crate>, error::ParseListOutputError> {
68        use error::ParseListOutputError;
69
70        if line.starts_with(" ") {
71            return Ok(None);
72        }
73
74        let mut parts = line.split(" ");
75        let name = parts.next().ok_or(ParseListOutputError)?;
76
77        let version = parts.next().ok_or(ParseListOutputError)?;
78        if !version.starts_with("v") {
79            return Err(ParseListOutputError);
80        }
81        let version = version.trim_start_matches("v");
82
83        if version.ends_with(":") {
84            // crates.io dependency
85            let version = version.trim_end_matches(":");
86            Ok(Some(Crate {
87                name: name.into(),
88                version: version.parse().map_err(|_| ParseListOutputError)?,
89                kind: CrateKind::CratesIo,
90            }))
91        } else {
92            let dependency_path = parts.next().ok_or(ParseListOutputError)?;
93            if !dependency_path.starts_with("(") || !dependency_path.ends_with("):") {
94                return Err(ParseListOutputError);
95            }
96            let dependency_path = dependency_path
97                .trim_start_matches("(")
98                .trim_end_matches("):");
99
100            if dependency_path.starts_with("http") {
101                // git dependency
102                writeln!(
103                    stderr(),
104                    "Warning: Git binaries are not supported. Ignoring `{}`.",
105                    name
106                );
107                Ok(None)
108            } else {
109                // local dependency
110                writeln!(
111                    stderr(),
112                    "Warning: Local binaries are not supported. Ignoring `{}`.",
113                    name
114                );
115                Ok(None)
116            }
117        }
118    }
119}
120
121pub mod error {
122    #[derive(Debug)]
123    pub struct ParseListOutputError;
124}