use crate::errors::BumperError; use cargo_edit::Manifest; use log::{debug, info}; use std::path::{Path, PathBuf}; use std::process::Command; use toml_edit::{Item, Value}; pub struct Rust { dirname: PathBuf, cargo_toml: CargoToml, } impl Rust { pub fn new(dirname: &Path) -> Result { let cargo_toml = dirname.join("Cargo.toml"); debug!( "does {} exist? {}", cargo_toml.display(), cargo_toml.exists() ); if cargo_toml.exists() { return Ok(Self { dirname: dirname.to_path_buf(), cargo_toml: CargoToml::parse(&cargo_toml)?, }); } Err(BumperError::UnknownProjectKind(dirname.to_path_buf())) } pub fn name(&mut self) -> Result { self.cargo_toml.name() } pub fn set_version(&mut self, version: &str) -> Result { debug!("parsing Cargo.toml from {}", self.dirname.display()); // debug!("Cargo.toml:\n{:#?}", self.cargo_toml); self.cargo_toml.set_version(version)?; self.cargo_toml.write()?; self.update_cargo_lock()?; let dirname = self .dirname .canonicalize() .map_err(|err| BumperError::AbsPath(self.dirname.to_path_buf(), err))?; info!( "Rust project {}, in {}, version set to {}", self.cargo_toml.name()?, dirname.display(), version ); Ok(version.to_string()) } fn update_cargo_lock(&self) -> Result<(), BumperError> { info!("running cargo update in {}", self.dirname.display()); let output = Command::new("cargo") .arg("update") .current_dir(&self.dirname) .output() .map_err(|err| BumperError::CargoInvoke(self.dirname.to_path_buf(), err))?; debug!("git exit code was {:?}", output.status.code()); if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr).into_owned(); return Err(BumperError::Cargo(self.dirname.to_path_buf(), stderr)); } Ok(()) } } #[derive(Debug)] struct CargoToml { dirname: PathBuf, manifest: Manifest, } impl CargoToml { fn parse(dirname: &Path) -> Result { let manifest = Manifest::open(&Some(dirname.to_path_buf())) .map_err(|err| BumperError::FromToml(dirname.to_path_buf(), err))?; Ok(Self { dirname: dirname.to_path_buf(), manifest, }) } fn get_package_table(&mut self) -> Result<&mut toml_edit::Table, BumperError> { let package = self .manifest .get_table(&["package".to_string()]) .map_err(BumperError::NoPackage)?; if let Some(table) = package.as_table_mut() { Ok(table) } else { Err(BumperError::ProjectNotTable) } } fn name(&mut self) -> Result { let package = self.get_package_table()?; let v = package.entry("name"); if let Some(name) = v.as_str() { Ok(name.to_string()) } else { Err(BumperError::UnnamedProject) } } fn set_version(&mut self, version: &str) -> Result<(), BumperError> { let package = self.get_package_table()?; debug!("setting Cargo.toml set version to {} in memory", version); let v = package.entry("version"); *v = Item::Value(Value::from(version)); Ok(()) } fn write(&self) -> Result<(), BumperError> { debug!("saving Cargo.toml to {}", self.dirname.display()); let mut file = Manifest::find_file(&Some(self.dirname.to_path_buf())) .map_err(|err| BumperError::CargoTomlNotFound(self.dirname.to_path_buf(), err))?; self.manifest .write_to_file(&mut file) .map_err(|err| BumperError::WriteToml(self.dirname.to_path_buf(), err))?; Ok(()) } }