1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
use std::{fmt, process::Command, str::FromStr}; use crate::Result; pub const VERSION_KEY: &str = "%version"; pub struct Hook(Vec<String>); impl FromStr for Hook { type Err = anyhow::Error; fn from_str(s: &str) -> Result<Self, Self::Err> { if s.is_empty() { bail!("hook must not be an empty string") } let words = shell_words::split(s)?; Ok(Hook(words)) } } impl fmt::Display for Hook { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let command = shell_words::join(self.0.iter()); f.write_str(&command) } } impl Hook { pub fn insert_version(&mut self, value: &str) { let entries_with_version = self .0 .iter() .map(|entry| entry.replace(VERSION_KEY, value)) .collect(); self.0 = entries_with_version } pub fn run(&self) -> Result<()> { let (cmd, args) = self.0.split_first().expect("hook must not be empty"); let status = Command::new(&cmd).args(args).status()?; if !status.success() { Err(anyhow!("hook failed with status {}", status)) } else { Ok(()) } } } #[cfg(test)] mod test { use super::Hook; use crate::Result; use std::str::FromStr; #[test] fn parse_empty_string() { assert!(Hook::from_str("").is_err()) } #[test] fn parse_valid_string() -> Result<()> { let hook = Hook::from_str("cargo bump %version")?; assert_eq!( &hook.0, &["cargo".to_string(), "bump".into(), "%version".into()] ); Ok(()) } #[test] fn replace_version_cargo() -> Result<()> { let mut hook = Hook::from_str("cargo bump %version")?; hook.insert_version("1.0.0"); assert_eq!( &hook.0, &["cargo".to_string(), "bump".into(), "1.0.0".into()] ); Ok(()) } #[test] fn replace_version_maven() -> Result<()> { let mut hook = Hook::from_str("mvn versions:set -DnewVersion=%version")?; hook.insert_version("1.0.0"); assert_eq!( &hook.0, &[ "mvn".to_string(), "versions:set".into(), "-DnewVersion=1.0.0".into() ] ); Ok(()) } #[test] fn leave_hook_untouched_when_no_version() -> Result<()> { let mut hook = Hook::from_str("echo \"Hello World\"")?; hook.insert_version("1.0.0"); assert_eq!(&hook.0, &["echo".to_string(), "Hello World".into()]); Ok(()) } #[test] fn replace_quoted_version() -> Result<()> { let mut hook = Hook::from_str("echo \"%version\"")?; hook.insert_version("1.0.0"); assert_eq!(&hook.0, &["echo".to_string(), "1.0.0".into()]); Ok(()) } }