diff --git a/src/hyperfine/app.rs b/src/hyperfine/app.rs index b24d38a..2e60b65 100644 --- a/src/hyperfine/app.rs +++ b/src/hyperfine/app.rs @@ -107,6 +107,17 @@ fn build_app() -> App<'static, 'static> { string '{VAR}' in each command by the current parameter value.", ), ) + .arg( + Arg::with_name("parameter-step-size") + .long("parameter-step-size") + .takes_value(true) + .value_names(&["STEP_SIZE"]) + .requires("parameter-scan") + .help( + "This argument requires --parameter-scan to be sepcified as well.\ + Traverse the range MIN..MAX in steps of STEP_SIZE.", + ), + ) .arg( Arg::with_name("style") .long("style") diff --git a/src/hyperfine/error.rs b/src/hyperfine/error.rs index d1fe060..cc0df70 100644 --- a/src/hyperfine/error.rs +++ b/src/hyperfine/error.rs @@ -7,6 +7,7 @@ pub enum ParameterScanError { ParseIntError(num::ParseIntError), EmptyRange, TooLarge, + ZeroStep, } impl From for ParameterScanError { @@ -27,6 +28,7 @@ impl Error for ParameterScanError { ParameterScanError::ParseIntError(ref e) => e.description(), ParameterScanError::EmptyRange => "Empty parameter range", ParameterScanError::TooLarge => "Parameter range is too large", + ParameterScanError::ZeroStep => "Zero is not a valid parameter step", } } } diff --git a/src/main.rs b/src/main.rs index 61fc8c0..998a8cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::cmp; use std::env; use std::error::Error; use std::io; +use std::iter::StepBy; use std::ops::Range; use atty::Stream; @@ -46,11 +47,12 @@ fn run( /// A function to read the `--parameter-scan` arguments fn parse_parameter_scan_args<'a>( - mut vals: clap::Values<'a>, -) -> Result<(&'a str, Range), ParameterScanError> { + mut vals: clap::Values<'a>, step_size: &str +) -> Result<(&'a str, StepBy>), ParameterScanError> { let param_name = vals.next().unwrap(); let param_min: i32 = vals.next().unwrap().parse()?; let param_max: i32 = vals.next().unwrap().parse()?; + let step_size = step_size.parse()?; const MAX_PARAMETERS: i32 = 100_000; if param_max - param_min > MAX_PARAMETERS { @@ -61,7 +63,14 @@ fn parse_parameter_scan_args<'a>( return Err(ParameterScanError::EmptyRange); } - Ok((param_name, param_min..(param_max + 1))) + if step_size == 0 { + // Iterator::step_by panics for zero step + return Err(ParameterScanError::ZeroStep); + } + + let param_range = (param_min..(param_max + 1)).step_by(step_size); + + Ok((param_name, param_range)) } fn main() { @@ -206,11 +215,12 @@ fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec> { let command_strings = matches.values_of("command").unwrap(); if let Some(args) = matches.values_of("parameter-scan") { - match parse_parameter_scan_args(args) { + let step_size = matches.value_of("parameter-step-size").unwrap_or("1"); + match parse_parameter_scan_args(args, step_size) { Ok((param_name, param_range)) => { let mut commands = vec![]; let command_strings = command_strings.collect::>(); - for value in param_range.start..param_range.end { + for value in param_range { for cmd in &command_strings { commands.push(Command::new_parametrized(cmd, param_name, value)); }