diff --git a/src/hyperfine/benchmark.rs b/src/hyperfine/benchmark.rs index 926b41e..759274c 100644 --- a/src/hyperfine/benchmark.rs +++ b/src/hyperfine/benchmark.rs @@ -225,7 +225,9 @@ pub fn run_benchmark( &values[num] }; match cmd.get_parameter() { - Some((param, value)) => Command::new_parametrized(preparation_command, param, *value), + Some((param, value)) => { + Command::new_parametrized(preparation_command, param, value.clone()) + } None => Command::new(preparation_command), } }); @@ -418,7 +420,9 @@ pub fn run_benchmark( .cleanup_command .as_ref() .map(|cleanup_command| match cmd.get_parameter() { - Some((param, value)) => Command::new_parametrized(cleanup_command, param, *value), + Some((param, value)) => { + Command::new_parametrized(cleanup_command, param, value.clone()) + } None => Command::new(cleanup_command), }); run_cleanup_command(&options.shell, &cleanup_cmd, options.show_output)?; @@ -433,6 +437,6 @@ pub fn run_benchmark( t_min, t_max, times_real, - cmd.get_parameter().map(|p| p.1.to_string()), + cmd.get_parameter().as_ref().map(|p| p.1.to_string()), )) } diff --git a/src/hyperfine/internal.rs b/src/hyperfine/internal.rs index f074a1f..ad6a0ec 100644 --- a/src/hyperfine/internal.rs +++ b/src/hyperfine/internal.rs @@ -148,3 +148,72 @@ fn test_compute_relative_speed() { assert_relative_eq!(1.0, annotated_results[1].relative_speed); assert_relative_eq!(2.5, annotated_results[2].relative_speed); } + +pub fn tokenize<'a>(values: &'a str) -> Vec { + if values == "" { + return vec![]; + } + + let mut tokens = vec![]; + let mut buf = String::new(); + + let mut iter = values.chars(); + while let Some(c) = iter.next() { + match c { + '\\' => match iter.next() { + Some(c2 @ ',') | Some(c2 @ '\\') => { + buf.push(c2); + } + Some(c2) => { + buf.push('\\'); + buf.push(c2); + } + None => buf.push('\\'), + }, + ',' => { + tokens.push(buf); + buf = String::new(); + } + _ => { + buf.push(c); + } + }; + } + + tokens.push(buf); + + tokens +} + +#[test] +fn test_tokenize_empty() { + assert!(tokenize("").is_empty()); +} + +#[test] +fn test_tokenize_single_value() { + assert_eq!(tokenize(r"foo"), vec!["foo"]); + assert_eq!(tokenize(r" "), vec![" "]); + assert_eq!(tokenize(r"hello\, world!"), vec!["hello, world!"]); + assert_eq!(tokenize(r"\,"), vec![","]); + assert_eq!(tokenize(r"\,\,\,"), vec![",,,"]); + assert_eq!(tokenize(r"\n"), vec![r"\n"]); + assert_eq!(tokenize(r"\\"), vec![r"\"]); + assert_eq!(tokenize(r"\\\,"), vec![r"\,"]); +} + +#[test] +fn test_tokenize_multiple_values() { + assert_eq!(tokenize(r"foo,bar,baz"), vec!["foo", "bar", "baz"]); + assert_eq!(tokenize(r"hello world,foo"), vec!["hello world", "foo"]); + + assert_eq!(tokenize(r"hello\,world!,baz"), vec!["hello,world!", "baz"]); +} + +#[test] +fn test_tokenize_empty_values() { + assert_eq!(tokenize(r"foo,,bar"), vec!["foo", "", "bar"]); + assert_eq!(tokenize(r",bar"), vec!["", "bar"]); + assert_eq!(tokenize(r"bar,"), vec!["bar", ""]); + assert_eq!(tokenize(r",,"), vec!["", "", ""]); +} diff --git a/src/hyperfine/types.rs b/src/hyperfine/types.rs index c34b029..f65fa07 100644 --- a/src/hyperfine/types.rs +++ b/src/hyperfine/types.rs @@ -39,16 +39,16 @@ impl Into for Decimal { } } -#[derive(Debug, Clone, Copy)] -pub enum ParameterValue<'a> { - Text(&'a str), +#[derive(Debug, Clone)] +pub enum ParameterValue { + Text(String), Numeric(NumericType), } -impl<'a> ToString for ParameterValue<'a> { +impl<'a> ToString for ParameterValue { fn to_string(&self) -> String { match self { - ParameterValue::Text(value) => value.to_string(), + ParameterValue::Text(ref value) => value.clone(), ParameterValue::Numeric(value) => value.to_string(), } } @@ -61,7 +61,7 @@ pub struct Command<'a> { expression: &'a str, /// A possible parameter value. - parameter: Option<(&'a str, ParameterValue<'a>)>, + parameter: Option<(&'a str, ParameterValue)>, } impl<'a> Command<'a> { @@ -75,7 +75,7 @@ impl<'a> Command<'a> { pub fn new_parametrized( expression: &'a str, parameter: &'a str, - value: ParameterValue<'a>, + value: ParameterValue, ) -> Command<'a> { Command { expression, @@ -93,7 +93,7 @@ impl<'a> Command<'a> { } } - pub fn get_parameter(&self) -> &Option<(&'a str, ParameterValue<'a>)> { + pub fn get_parameter(&self) -> &Option<(&'a str, ParameterValue)> { &self.parameter } } diff --git a/src/main.rs b/src/main.rs index 40f0668..e3496ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use crate::hyperfine::app::get_arg_matches; use crate::hyperfine::benchmark::{mean_shell_spawning_time, run_benchmark}; use crate::hyperfine::error::OptionsError; use crate::hyperfine::export::{ExportManager, ExportType}; -use crate::hyperfine::internal::write_benchmark_comparison; +use crate::hyperfine::internal::{tokenize, write_benchmark_comparison}; use crate::hyperfine::parameter_range::get_parameterized_commands; use crate::hyperfine::types::{ BenchmarkResult, CmdFailureAction, Command, HyperfineOptions, OutputStyleOption, ParameterValue, @@ -212,7 +212,7 @@ fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec> { let param_name = args.next().unwrap(); let param_list_str = args.next().unwrap(); - let param_list = param_list_str.split(',').collect::>(); + let param_list = tokenize(param_list_str); let command_list = command_strings.collect::>(); let mut commands = Vec::with_capacity(param_list.len() * command_list.len()); @@ -222,7 +222,7 @@ fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec> { commands.push(Command::new_parametrized( cmd, param_name, - ParameterValue::Text(value), + ParameterValue::Text(value.clone()), )); } }