diff --git a/src/lib.rs b/src/lib.rs index 8696f06..2dbb74c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::io::{stderr, Write}; use std::process::{Command, ExitStatus}; @@ -34,121 +34,10 @@ pub fn installed_crates() -> Result, String> { Ok(crates) } -pub fn get_latest_versions( - required_crates: &HashMap, -) -> Result, String> { - use std::fs; - use tempfile::TempDir; - - fn dependency_string(required_crates: &HashMap) -> String { - let mut string = String::new(); - for c in required_crates.values() { - match c.kind { - CrateKind::CratesIo => { - string.push_str(&format!(r#"{} = "{}"{}"#, c.name, c.version, '\n')); - } - } - } - string - } - - fn create_dummy_crate(required_crates: &HashMap) -> Result { - let tmpdir = TempDir::new() - .map_err(|e| format!("I/O Error while creating temporary directory: {}", e))?; - let cargo_toml_path = tmpdir.path().join("Cargo.toml"); - let src_dir_path = tmpdir.path().join("src"); - let lib_rs_path = src_dir_path.join("lib.rs"); - - let cargo_toml_content = format!( - r#"[package] -name = "cargo-update-installed-dummy" -version = "0.1.0" -authors = [""] - -[dependencies] -{} -"#, - dependency_string(required_crates) - ); - - fs::create_dir(src_dir_path) - .map_err(|e| format!("I/O Error while creating src dir in temp dir: {}", e))?; - fs::write(cargo_toml_path, cargo_toml_content) - .map_err(|e| format!("I/O Error while writing dummy Cargo.toml: {}", e))?; - fs::write(lib_rs_path, "") - .map_err(|e| format!("I/O Error while writing dummy lib.rs: {}", e))?; - Ok(tmpdir) - } - - fn run_cargo_update(tmpdir: &TempDir) -> Result { - let mut cargo_update_command = Command::new("cargo"); - cargo_update_command.arg("update"); - cargo_update_command.arg("--manifest-path"); - cargo_update_command.arg(tmpdir.path().join("Cargo.toml")); - cargo_update_command - .status() - .map_err(|e| format!("I/O Error while running `cargo update`: {}", e)) - } - - fn parse_cargo_lock( - tmpdir: &TempDir, - required_crates: &HashMap, - ) -> Result, String> { - use std::fs; - use toml::Value; - - let cargo_lock_path = tmpdir.path().join("Cargo.lock"); - let cargo_lock = fs::read_to_string(cargo_lock_path) - .map_err(|e| format!("I/O Error while reading dummy Cargo.lock: {}", e))?; - - let root_value: Value = cargo_lock - .parse() - .map_err(|e| format!("Error while parsing dummy Cargo.lock: {}", e))?; - let packages = root_value - .get("package") - .and_then(|v| v.as_array()) - .ok_or("Error: package array not found in dummy Cargo.lock")?; - - let mut latest_versions = HashMap::new(); - for crate_name in required_crates.keys() { - let package = packages - .iter() - .find(|p| p.get("name").and_then(|v| v.as_str()) == Some(crate_name)) - .ok_or(format!( - "Error: package {} not found in dummy Cargo.lock", - crate_name - ))?; - let version = package - .get("version") - .and_then(|v| v.as_str()) - .ok_or(format!( - "Error: package {} has no version number in dummy Cargo.lock", - crate_name - ))?; - if latest_versions - .insert(crate_name.clone(), String::from(version)) - .is_some() - { - writeln!(stderr(), "Warning: package {} is present multiple times in dummy Cargo.lock. Choosing version {}.", crate_name, version); - } - } - Ok(latest_versions) - } - - let tmpdir = create_dummy_crate(required_crates)?; - if !run_cargo_update(&tmpdir)?.success() { - return Err("Error: `cargo update` failed".into()); - } - parse_cargo_lock(&tmpdir, required_crates) -} - -pub fn install_update(name: &str, version: &str) -> Result { +pub fn install_update(name: &str) -> Result { let mut cargo_install_command = Command::new("cargo"); cargo_install_command.arg("install"); - cargo_install_command.arg("--force"); cargo_install_command.arg(name); - cargo_install_command.arg("--version"); - cargo_install_command.arg(version); cargo_install_command .status() .map_err(|e| format!("I/O Error while running `cargo install`: {}", e)) @@ -196,7 +85,7 @@ impl Crate { let version = version.trim_end_matches(":"); Ok(Some(Crate { name: name.into(), - version: version.into(), + version: version.parse().map_err(|_| ParseListOutputError)?, kind: CrateKind::CratesIo, })) } else { diff --git a/src/main.rs b/src/main.rs index 8b75ed4..7b23351 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,60 +14,14 @@ fn main() { } fn run() -> Result<(), String> { - use std::collections::HashMap; - let installed_crates = installed_crates()?; - let mut required_crates = HashMap::new(); - for c in installed_crates.values() { - if required_crates.contains_key(&c.name) { - println!("Ignoring duplicate installed crate: {:?}", c); - continue; - } - let mut required_crate = c.clone(); - // require latest version with no constraints - required_crate.version = "*".into(); - if required_crates - .insert(c.name.clone(), required_crate) - .is_some() - { - unreachable!("duplicate key"); - } - } - - let latest_versions = get_latest_versions(&required_crates)?; - - let mut updates = Vec::new(); - for c in installed_crates.values() { - let latest_version = latest_versions - .get(&c.name) - .ok_or(format!("Error: No latest version found for {}", c.name))?; - if &c.version != latest_version { - updates.push((c, latest_version)); - } else { - println!("Up to date: {} {}", c.name, c.version); - } - } - updates.sort_unstable_by_key(|(c, _)| &c.name); - - if updates.len() > 1 { - println!("\nThe following updates will be performed:"); - for (_crate, latest_version) in &updates { - println!(" {} from {} to {}", _crate.name, _crate.version, latest_version); - } - } - - for (_crate, latest_version) in &updates { - println!( - "\nUpdating {} from {} to {}", - _crate.name, _crate.version, latest_version - ); - if !install_update(&_crate.name, latest_version)?.success() { + for c in installed_crates.keys() { + println!("Updating `{c}`"); + if !install_update(c)?.success() { return Err("Error: `cargo install` failed".into()); } } - println!("\nAll packages up to date."); - Ok(()) }