use std::io::Write; use crate::{actions::Action, ActionConfig}; #[derive(Default, Clone)] pub struct RustAction {} impl RustAction { pub fn new() -> Self { Self {} } fn execute_content( &self, version: &semver::Version, cargo_content: &str, ) -> anyhow::Result { tracing::trace!("parsing Cargo.toml file as tolm"); let mut cargo_doc = cargo_content.parse::()?; tracing::debug!( "updating cargo workspace package version to {}", version.to_string() ); let workspace = if cargo_doc.contains_table("workspace") { cargo_doc["workspace"].as_table_mut().unwrap() } else { let mut t = toml_edit::Table::new(); t.set_implicit(true); cargo_doc["workspace"] = toml_edit::Item::Table(t); cargo_doc["workspace"].as_table_mut().unwrap() }; let package = workspace["package"].or_insert(toml_edit::table()); package["version"] = toml_edit::value(version.to_string()); Ok(cargo_doc.to_string()) } } impl Action for RustAction { fn enabled(&self, config: &ActionConfig) -> anyhow::Result { if let Ok(v) = std::env::var("CUDDLE_PLEASE_RUST_ACTION") { if let Ok(true) = v.parse::() { return Ok(true); } } let val = &config[self.name().as_str()]; if val.is_badvalue() { return Ok(false); } Ok(val.as_bool().unwrap_or(true)) } fn name(&self) -> String { "rust".into() } fn execute(&self, version: &semver::Version) -> anyhow::Result<()> { tracing::info!( "running rust action for version: {} and file: Cargo.toml", version.to_string() ); let path = std::path::PathBuf::from("Cargo.toml"); tracing::trace!("reading Cargo.toml"); let file = match std::fs::read_to_string(&path) { Ok(file) => file, Err(e) => match e.kind() { std::io::ErrorKind::NotFound => { anyhow::bail!("err: Cargo.toml was not found in dir") } _ => Err(e)?, }, }; let cargo_doc = self.execute_content(version, &file)?; let mut cargo_file = std::fs::File::create(&path)?; cargo_file.write_all(cargo_doc.as_bytes())?; cargo_file.sync_all()?; tracing::debug!("finished writing cargo file"); Ok(()) } } #[cfg(test)] mod test { use semver::{BuildMetadata, Prerelease}; use crate::{actions::Action, ActionConfig}; use super::RustAction; #[test] fn test_is_enabled() { let config = ActionConfig::try_from( r#" please: actions: rust: true "#, ) .unwrap(); let enabled = RustAction::new().enabled(&config).unwrap(); assert!(enabled) } #[test] fn test_is_disabled_by_default() { let config = ActionConfig::try_from( r#" please: "#, ) .unwrap(); let enabled = RustAction::new().enabled(&config).unwrap(); assert!(!enabled) } #[test] fn test_is_disabled() { let config = ActionConfig::try_from( r#" please: actions: rust: false "#, ) .unwrap(); let enabled = RustAction::new().enabled(&config).unwrap(); assert!(!enabled) } #[test] fn test_missing_value_is_enabled() { let config = ActionConfig::try_from( r#" please: actions: rust: "#, ) .unwrap(); let enabled = RustAction::new().enabled(&config).unwrap(); assert!(enabled) } #[test] fn test_can_edit_empty_file() { let output = RustAction::default() .execute_content( &semver::Version { major: 0, minor: 1, patch: 0, pre: Prerelease::default(), build: BuildMetadata::default(), }, "", ) .unwrap(); pretty_assertions::assert_eq!( r#"[workspace.package] version = "0.1.0" "#, &output, ) } #[test] fn test_only_edits_stuff() { let input = r#" [workspace] members = ["."] [package] something = {some = "something"} # Some comment [workspace.package] version = "0.0.0" # some comment readme = "../../" "#; let output = RustAction::default() .execute_content( &semver::Version { major: 0, minor: 1, patch: 0, pre: Prerelease::default(), build: BuildMetadata::default(), }, input, ) .unwrap(); pretty_assertions::assert_eq!( r#" [workspace] members = ["."] [package] something = {some = "something"} # Some comment [workspace.package] version = "0.1.0" readme = "../../" "#, &output, ) } }