mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-10-28 08:08:58 +00:00
Introduce Commands struct
This commit is contained in:
1 parent
c5a2266199
commit
a6baf49b72
4 files changed
+139
-125
No files matched your search
+1
-1
@@ -2,7 +2,7 @@ use std::ffi::OsString;
|
||||
|
||||
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
|
||||
|
||||
pub fn get_arg_matches<'a, I, T>(args: I) -> ArgMatches
|
||||
pub fn get_cli_arguments<'a, I, T>(args: I) -> ArgMatches
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone + 'a,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::command::Commands;
|
||||
use crate::export::ExportManager;
|
||||
use crate::options::{Options, OutputStyleOption};
|
||||
use crate::{command::Command, export::ExportManager};
|
||||
|
||||
use super::{mean_shell_spawning_time, relative_speed, result::BenchmarkResult, run_benchmark};
|
||||
|
||||
@@ -45,7 +46,7 @@ pub fn write_benchmark_comparison(results: &[BenchmarkResult]) {
|
||||
}
|
||||
|
||||
pub fn run_benchmarks_and_print_comparison(
|
||||
commands: &[Command<'_>],
|
||||
commands: &Commands,
|
||||
options: &Options,
|
||||
export_manager: &ExportManager,
|
||||
) -> Result<()> {
|
||||
|
||||
+129
-116
@@ -90,115 +90,9 @@ impl<'a> Command<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the commands to benchmark
|
||||
pub fn build_commands(matches: &ArgMatches) -> Result<Vec<Command>> {
|
||||
let command_names = matches.values_of("command-name");
|
||||
let command_strings = matches.values_of("command").unwrap();
|
||||
|
||||
if let Some(args) = matches.values_of("parameter-scan") {
|
||||
let step_size = matches.value_of("parameter-step-size");
|
||||
Ok(get_parameterized_commands(
|
||||
command_names,
|
||||
command_strings,
|
||||
args,
|
||||
step_size,
|
||||
)?)
|
||||
} else if let Some(args) = matches.values_of("parameter-list") {
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
|
||||
let args: Vec<_> = args.collect();
|
||||
let param_names_and_values: Vec<(&str, Vec<String>)> = args
|
||||
.chunks_exact(2)
|
||||
.map(|pair| {
|
||||
let name = pair[0];
|
||||
let list_str = pair[1];
|
||||
(name, tokenize(list_str))
|
||||
})
|
||||
.collect();
|
||||
{
|
||||
let dupes = find_dupes(param_names_and_values.iter().map(|(name, _)| *name));
|
||||
if !dupes.is_empty() {
|
||||
bail!("Duplicate parameter names: {}", &dupes.join(", "));
|
||||
}
|
||||
}
|
||||
let command_list = command_strings.collect::<Vec<&str>>();
|
||||
|
||||
let dimensions: Vec<usize> = std::iter::once(command_list.len())
|
||||
.chain(
|
||||
param_names_and_values
|
||||
.iter()
|
||||
.map(|(_, values)| values.len()),
|
||||
)
|
||||
.collect();
|
||||
let param_space_size = dimensions.iter().product();
|
||||
if param_space_size == 0 {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// `--command-name` should appear exactly once or exactly B times,
|
||||
// where B is the total number of benchmarks.
|
||||
let command_name_count = command_names.len();
|
||||
if command_name_count > 1 && command_name_count != param_space_size {
|
||||
return Err(OptionsError::UnexpectedCommandNameCount(
|
||||
command_name_count,
|
||||
param_space_size,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let mut commands = Vec::with_capacity(param_space_size);
|
||||
let mut index = vec![0usize; dimensions.len()];
|
||||
'outer: loop {
|
||||
let name = command_names
|
||||
.get(i)
|
||||
.or_else(|| command_names.get(0))
|
||||
.copied();
|
||||
i += 1;
|
||||
|
||||
let (command_index, params_indices) = index.split_first().unwrap();
|
||||
let parameters = param_names_and_values
|
||||
.iter()
|
||||
.zip(params_indices)
|
||||
.map(|((name, values), i)| (*name, ParameterValue::Text(values[*i].clone())))
|
||||
.collect();
|
||||
commands.push(Command::new_parametrized(
|
||||
name,
|
||||
command_list[*command_index],
|
||||
parameters,
|
||||
));
|
||||
|
||||
// Increment index, exiting loop on overflow.
|
||||
for (i, n) in index.iter_mut().zip(dimensions.iter()) {
|
||||
*i += 1;
|
||||
if *i < *n {
|
||||
continue 'outer;
|
||||
} else {
|
||||
*i = 0;
|
||||
}
|
||||
}
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
Ok(commands)
|
||||
} else {
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
if command_names.len() > command_strings.len() {
|
||||
return Err(OptionsError::TooManyCommandNames(command_strings.len()).into());
|
||||
}
|
||||
|
||||
let command_list = command_strings.collect::<Vec<&str>>();
|
||||
let mut commands = Vec::with_capacity(command_list.len());
|
||||
for (i, s) in command_list.iter().enumerate() {
|
||||
commands.push(Command::new(command_names.get(i).copied(), s));
|
||||
}
|
||||
Ok(commands)
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds all the strings that appear multiple times in the input iterator, returning them in
|
||||
/// sorted order. If no string appears more than once, the result is an empty vector.
|
||||
fn find_dupes<'a, I: IntoIterator<Item = &'a str>>(i: I) -> Vec<&'a str> {
|
||||
fn find_duplicates<'a, I: IntoIterator<Item = &'a str>>(i: I) -> Vec<&'a str> {
|
||||
let mut counts = BTreeMap::<&'a str, usize>::new();
|
||||
for s in i {
|
||||
*counts.entry(s).or_default() += 1;
|
||||
@@ -209,6 +103,125 @@ fn find_dupes<'a, I: IntoIterator<Item = &'a str>>(i: I) -> Vec<&'a str> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub struct Commands<'a>(Vec<Command<'a>>);
|
||||
|
||||
impl<'a> Commands<'a> {
|
||||
/// Build the commands to benchmark
|
||||
pub fn from_cli_arguments(matches: &'a ArgMatches) -> Result<Commands> {
|
||||
let command_names = matches.values_of("command-name");
|
||||
let command_strings = matches.values_of("command").unwrap();
|
||||
|
||||
if let Some(args) = matches.values_of("parameter-scan") {
|
||||
let step_size = matches.value_of("parameter-step-size");
|
||||
Ok(Self(get_parameterized_commands(
|
||||
command_names,
|
||||
command_strings,
|
||||
args,
|
||||
step_size,
|
||||
)?))
|
||||
} else if let Some(args) = matches.values_of("parameter-list") {
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
|
||||
let args: Vec<_> = args.collect();
|
||||
let param_names_and_values: Vec<(&str, Vec<String>)> = args
|
||||
.chunks_exact(2)
|
||||
.map(|pair| {
|
||||
let name = pair[0];
|
||||
let list_str = pair[1];
|
||||
(name, tokenize(list_str))
|
||||
})
|
||||
.collect();
|
||||
{
|
||||
let duplicates =
|
||||
find_duplicates(param_names_and_values.iter().map(|(name, _)| *name));
|
||||
if !duplicates.is_empty() {
|
||||
bail!("Duplicate parameter names: {}", &duplicates.join(", "));
|
||||
}
|
||||
}
|
||||
let command_list = command_strings.collect::<Vec<&str>>();
|
||||
|
||||
let dimensions: Vec<usize> = std::iter::once(command_list.len())
|
||||
.chain(
|
||||
param_names_and_values
|
||||
.iter()
|
||||
.map(|(_, values)| values.len()),
|
||||
)
|
||||
.collect();
|
||||
let param_space_size = dimensions.iter().product();
|
||||
if param_space_size == 0 {
|
||||
return Ok(Self(Vec::new()));
|
||||
}
|
||||
|
||||
// `--command-name` should appear exactly once or exactly B times,
|
||||
// where B is the total number of benchmarks.
|
||||
let command_name_count = command_names.len();
|
||||
if command_name_count > 1 && command_name_count != param_space_size {
|
||||
return Err(OptionsError::UnexpectedCommandNameCount(
|
||||
command_name_count,
|
||||
param_space_size,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let mut commands = Vec::with_capacity(param_space_size);
|
||||
let mut index = vec![0usize; dimensions.len()];
|
||||
'outer: loop {
|
||||
let name = command_names
|
||||
.get(i)
|
||||
.or_else(|| command_names.get(0))
|
||||
.copied();
|
||||
i += 1;
|
||||
|
||||
let (command_index, params_indices) = index.split_first().unwrap();
|
||||
let parameters = param_names_and_values
|
||||
.iter()
|
||||
.zip(params_indices)
|
||||
.map(|((name, values), i)| (*name, ParameterValue::Text(values[*i].clone())))
|
||||
.collect();
|
||||
commands.push(Command::new_parametrized(
|
||||
name,
|
||||
command_list[*command_index],
|
||||
parameters,
|
||||
));
|
||||
|
||||
// Increment index, exiting loop on overflow.
|
||||
for (i, n) in index.iter_mut().zip(dimensions.iter()) {
|
||||
*i += 1;
|
||||
if *i < *n {
|
||||
continue 'outer;
|
||||
} else {
|
||||
*i = 0;
|
||||
}
|
||||
}
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
Ok(Self(commands))
|
||||
} else {
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
if command_names.len() > command_strings.len() {
|
||||
return Err(OptionsError::TooManyCommandNames(command_strings.len()).into());
|
||||
}
|
||||
|
||||
let command_list = command_strings.collect::<Vec<&str>>();
|
||||
let mut commands = Vec::with_capacity(command_list.len());
|
||||
for (i, s) in command_list.iter().enumerate() {
|
||||
commands.push(Command::new(command_names.get(i).copied(), s));
|
||||
}
|
||||
Ok(Self(commands))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Command<'a>> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_shell_command_nonoverlapping() {
|
||||
let cmd = Command::new_parametrized(
|
||||
@@ -243,9 +256,9 @@ impl<'a> fmt::Display for Command<'a> {
|
||||
|
||||
#[test]
|
||||
fn test_build_commands_cross_product() {
|
||||
use crate::app::get_arg_matches;
|
||||
use crate::app::get_cli_arguments;
|
||||
|
||||
let matches = get_arg_matches(vec![
|
||||
let matches = get_cli_arguments(vec![
|
||||
"hyperfine",
|
||||
"-L",
|
||||
"par1",
|
||||
@@ -256,7 +269,7 @@ fn test_build_commands_cross_product() {
|
||||
"echo {par1} {par2}",
|
||||
"printf '%s\n' {par1} {par2}",
|
||||
]);
|
||||
let result = build_commands(&matches).unwrap();
|
||||
let result = Commands::from_cli_arguments(&matches).unwrap().0;
|
||||
|
||||
// Iteration order: command list first, then parameters in listed order (here, "par1" before
|
||||
// "par2", which is distinct from their sorted order), with parameter values in listed order.
|
||||
@@ -281,9 +294,9 @@ fn test_build_commands_cross_product() {
|
||||
|
||||
#[test]
|
||||
fn test_build_parameter_list_commands() {
|
||||
use crate::app::get_arg_matches;
|
||||
use crate::app::get_cli_arguments;
|
||||
|
||||
let matches = get_arg_matches(vec![
|
||||
let matches = get_cli_arguments(vec![
|
||||
"hyperfine",
|
||||
"echo {foo}",
|
||||
"--parameter-list",
|
||||
@@ -292,7 +305,7 @@ fn test_build_parameter_list_commands() {
|
||||
"--command-name",
|
||||
"name-{foo}",
|
||||
]);
|
||||
let commands = build_commands(&matches).unwrap();
|
||||
let commands = Commands::from_cli_arguments(&matches).unwrap().0;
|
||||
assert_eq!(commands.len(), 2);
|
||||
assert_eq!(commands[0].get_name(), "name-1");
|
||||
assert_eq!(commands[1].get_name(), "name-2");
|
||||
@@ -302,8 +315,8 @@ fn test_build_parameter_list_commands() {
|
||||
|
||||
#[test]
|
||||
fn test_build_parameter_range_commands() {
|
||||
use crate::app::get_arg_matches;
|
||||
let matches = get_arg_matches(vec![
|
||||
use crate::app::get_cli_arguments;
|
||||
let matches = get_cli_arguments(vec![
|
||||
"hyperfine",
|
||||
"echo {val}",
|
||||
"--parameter-scan",
|
||||
@@ -315,7 +328,7 @@ fn test_build_parameter_range_commands() {
|
||||
"--command-name",
|
||||
"name-{val}",
|
||||
]);
|
||||
let commands = build_commands(&matches).unwrap();
|
||||
let commands = Commands::from_cli_arguments(&matches).unwrap().0;
|
||||
assert_eq!(commands.len(), 2);
|
||||
assert_eq!(commands[0].get_name(), "name-1");
|
||||
assert_eq!(commands[1].get_name(), "name-2");
|
||||
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
use std::env;
|
||||
|
||||
use app::get_arg_matches;
|
||||
use app::get_cli_arguments;
|
||||
use benchmark::schedule::run_benchmarks_and_print_comparison;
|
||||
use command::build_commands;
|
||||
use command::Commands;
|
||||
use export::ExportManager;
|
||||
use options::Options;
|
||||
|
||||
@@ -27,10 +27,10 @@ fn run() -> Result<()> {
|
||||
#[cfg(windows)]
|
||||
colored::control::set_virtual_terminal(true).unwrap();
|
||||
|
||||
let matches = get_arg_matches(env::args_os());
|
||||
let options = Options::from_cli_arguments(&matches)?;
|
||||
let commands = build_commands(&matches)?;
|
||||
let export_manager = ExportManager::from_cli_arguments(&matches)?;
|
||||
let cli_arguments = get_cli_arguments(env::args_os());
|
||||
let options = Options::from_cli_arguments(&cli_arguments)?;
|
||||
let commands = Commands::from_cli_arguments(&cli_arguments)?;
|
||||
let export_manager = ExportManager::from_cli_arguments(&cli_arguments)?;
|
||||
|
||||
run_benchmarks_and_print_comparison(&commands, &options, &export_manager)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user