cargo_update_installed/
lib.rs1use 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 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 }
64
65impl Crate {
66 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 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 writeln!(
103 stderr(),
104 "Warning: Git binaries are not supported. Ignoring `{}`.",
105 name
106 );
107 Ok(None)
108 } else {
109 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}