diff --git a/src/extension/random.rs b/src/extension/random.rs index 909a696..6923a6e 100644 --- a/src/extension/random.rs +++ b/src/extension/random.rs @@ -34,7 +34,7 @@ impl super::Extension for RandomExtension { String::from("random") } - fn calculate(&self, params: &Mapping, _: &Vec) -> Option { // TODO: add argument handling + fn calculate(&self, params: &Mapping, args: &Vec) -> Option { let choices = params.get(&Value::from("choices")); if choices.is_none() { warn!("No 'choices' parameter specified for random variable"); @@ -51,7 +51,10 @@ impl super::Extension for RandomExtension { match choice { Some(output) => { - return Some(output.clone()) + // Render arguments + let output = crate::render::utils::render_args(output, args); + + return Some(output) }, None => { error!("Could not select a random choice."); @@ -91,5 +94,29 @@ mod tests { assert!(choices.iter().any(|x| x == &output)); } - // TODO: add test with arguments + #[test] + fn test_random_with_args() { + let mut params = Mapping::new(); + let choices = vec!( + "first $0$", + "second $0$", + "$0$ third", + ); + params.insert(Value::from("choices"), Value::from(choices.clone())); + + let extension = RandomExtension::new(); + let output = extension.calculate(¶ms, &vec!["test".to_owned()]); + + assert!(output.is_some()); + + let output = output.unwrap(); + + let rendered_choices = vec!( + "first test", + "second test", + "test third", + ); + + assert!(rendered_choices.iter().any(|x| x == &output)); + } } \ No newline at end of file diff --git a/src/extension/script.rs b/src/extension/script.rs index ecbc1bd..554fdf5 100644 --- a/src/extension/script.rs +++ b/src/extension/script.rs @@ -34,7 +34,7 @@ impl super::Extension for ScriptExtension { String::from("script") } - fn calculate(&self, params: &Mapping, _: &Vec) -> Option { // TODO: add argument handling + fn calculate(&self, params: &Mapping, user_args: &Vec) -> Option { let args = params.get(&Value::from("args")); if args.is_none() { warn!("No 'args' parameter specified for script variable"); @@ -42,10 +42,17 @@ impl super::Extension for ScriptExtension { } let args = args.unwrap().as_sequence(); if let Some(args) = args { - let str_args = args.iter().map(|arg| { + let mut str_args = args.iter().map(|arg| { arg.as_str().unwrap_or_default().to_string() }).collect::>(); + // The user has to enable argument concatenation explicitly + let inject_args = params.get(&Value::from("inject_args")) + .unwrap_or(&Value::from(false)).as_bool().unwrap_or(false); + if inject_args { + str_args.extend(user_args.clone()); + } + let output = if str_args.len() > 1 { Command::new(&str_args[0]) .args(&str_args[1..]) @@ -71,4 +78,62 @@ impl super::Extension for ScriptExtension { error!("Could not execute script with args '{:?}'", args); None } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::extension::Extension; + + #[test] + fn test_script_basic() { + let mut params = Mapping::new(); + params.insert(Value::from("args"), Value::from(vec!["echo", "hello world"])); + + let extension = ScriptExtension::new(); + let output = extension.calculate(¶ms, &vec![]); + + assert!(output.is_some()); + + if cfg!(target_os = "windows") { + assert_eq!(output.unwrap(), "hello world\r\n"); + }else{ + assert_eq!(output.unwrap(), "hello world\n"); + } + } + + #[test] + fn test_script_inject_args_off() { + let mut params = Mapping::new(); + params.insert(Value::from("args"), Value::from(vec!["echo", "hello world"])); + + let extension = ScriptExtension::new(); + let output = extension.calculate(¶ms, &vec!["jon".to_owned()]); + + assert!(output.is_some()); + + if cfg!(target_os = "windows") { + assert_eq!(output.unwrap(), "hello world\r\n"); + }else{ + assert_eq!(output.unwrap(), "hello world\n"); + } + } + + #[test] + fn test_script_inject_args_on() { + let mut params = Mapping::new(); + params.insert(Value::from("args"), Value::from(vec!["echo", "hello world"])); + params.insert(Value::from("inject_args"), Value::from(true)); + + let extension = ScriptExtension::new(); + let output = extension.calculate(¶ms, &vec!["jon".to_owned()]); + + assert!(output.is_some()); + + if cfg!(target_os = "windows") { + assert_eq!(output.unwrap(), "hello world jon\r\n"); + }else{ + assert_eq!(output.unwrap(), "hello world jon\n"); + } + } } \ No newline at end of file diff --git a/src/extension/shell.rs b/src/extension/shell.rs index 9736628..f8e014a 100644 --- a/src/extension/shell.rs +++ b/src/extension/shell.rs @@ -20,6 +20,15 @@ use serde_yaml::{Mapping, Value}; use std::process::Command; use log::{warn, error}; +use regex::{Regex, Captures}; + +lazy_static! { + static ref POS_ARG_REGEX: Regex = if cfg!(target_os = "windows") { + Regex::new("\\%(?P\\d+)").unwrap() + }else{ + Regex::new("\\$(?P\\d+)").unwrap() + }; +} pub struct ShellExtension {} @@ -34,7 +43,7 @@ impl super::Extension for ShellExtension { String::from("shell") } - fn calculate(&self, params: &Mapping, _: &Vec) -> Option { // TODO: add argument handling + fn calculate(&self, params: &Mapping, args: &Vec) -> Option { let cmd = params.get(&Value::from("cmd")); if cmd.is_none() { warn!("No 'cmd' parameter specified for shell variable"); @@ -42,14 +51,25 @@ impl super::Extension for ShellExtension { } let cmd = cmd.unwrap().as_str().unwrap(); + // Render positional parameters in args + let cmd = POS_ARG_REGEX.replace_all(&cmd, |caps: &Captures| { + let position_str = caps.name("pos").unwrap().as_str(); + let position = position_str.parse::().unwrap_or(-1); + if position >= 0 && position < args.len() as i32 { + args[position as usize].to_owned() + }else{ + "".to_owned() + } + }).to_string(); + let output = if cfg!(target_os = "windows") { Command::new("cmd") - .args(&["/C", cmd]) + .args(&["/C", &cmd]) .output() } else { Command::new("sh") .arg("-c") - .arg(cmd) + .arg(&cmd) .output() }; @@ -163,5 +183,31 @@ mod tests { assert_eq!(output.unwrap(), "hello world"); } - // TODO: add tests with arguments + #[test] + #[cfg(not(target_os = "windows"))] + fn test_shell_args_unix() { + let mut params = Mapping::new(); + params.insert(Value::from("cmd"), Value::from("echo $0")); + + let extension = ShellExtension::new(); + let output = extension.calculate(¶ms, &vec!["hello".to_owned()]); + + assert!(output.is_some()); + + assert_eq!(output.unwrap(), "hello\n"); + } + + #[test] + #[cfg(target_os = "windows")] + fn test_shell_args_windows() { + let mut params = Mapping::new(); + params.insert(Value::from("cmd"), Value::from("echo %0")); + + let extension = ShellExtension::new(); + let output = extension.calculate(¶ms, &vec!["hello".to_owned()]); + + assert!(output.is_some()); + + assert_eq!(output.unwrap(), "hello\r\n"); + } } \ No newline at end of file