use std::collections::HashSet;
use crate::__internal::error::{Error, Errors};
use crate::__internal::{
InternalBinaryBenchmark, InternalBinaryBenchmarkBench, InternalBinaryBenchmarkConfig,
InternalBinaryBenchmarkGroup, InternalBinaryBenchmarkGroups, InternalMacroBinBench, ModulePath,
};
use crate::{BenchmarkId, ValgrindTool};
pub type InternalMacroBinBenches = &'static [&'static (
&'static str,
fn() -> Option<InternalBinaryBenchmarkConfig>,
&'static [InternalMacroBinBench],
)];
#[derive(Debug)]
pub struct GroupsBuilder {
groups: InternalBinaryBenchmarkGroups,
errors: Errors,
}
impl GroupsBuilder {
pub fn new(
config: Option<InternalBinaryBenchmarkConfig>,
args: Vec<String>,
has_setup: bool,
has_teardown: bool,
) -> Self {
let groups = if cfg!(feature = "cachegrind") {
InternalBinaryBenchmarkGroups {
config: config.unwrap_or_default(),
groups: Vec::default(),
command_line_args: args,
has_setup,
has_teardown,
default_tool: ValgrindTool::Cachegrind,
}
} else {
InternalBinaryBenchmarkGroups {
config: config.unwrap_or_default(),
groups: Vec::default(),
command_line_args: args,
has_setup,
has_teardown,
default_tool: ValgrindTool::Callgrind,
}
};
Self {
groups,
errors: Errors::default(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn add_group(
&mut self,
group: crate::BinaryBenchmarkGroup,
id: String,
module_path: &str,
is_attribute: bool,
config: Option<InternalBinaryBenchmarkConfig>,
has_setup: bool,
has_teardown: bool,
compare_by_id: Option<bool>,
benches: InternalMacroBinBenches,
) {
let mut internal_group = InternalBinaryBenchmarkGroup {
id,
config,
has_setup,
has_teardown,
compare_by_id,
..Default::default()
};
if is_attribute {
Self::high_level(&mut internal_group, benches);
} else {
self.low_level(&mut internal_group, group, module_path);
}
self.groups.groups.push(internal_group);
}
fn high_level(group: &mut InternalBinaryBenchmarkGroup, benches: InternalMacroBinBenches) {
for (function_name, get_config, macro_bin_benches) in benches {
let mut internal_binary_benchmark = InternalBinaryBenchmark {
benches: vec![],
config: get_config(),
};
for macro_bin_bench in *macro_bin_benches {
let bench = InternalBinaryBenchmarkBench {
id: macro_bin_bench.id_display.map(ToString::to_string),
args: macro_bin_bench.args_display.map(ToString::to_string),
function_name: (*function_name).to_owned(),
command: (macro_bin_bench.func)().into(),
config: macro_bin_bench.config.map(|f| f()),
has_setup: macro_bin_bench.setup.is_some(),
has_teardown: macro_bin_bench.teardown.is_some(),
};
internal_binary_benchmark.benches.push(bench);
}
group.binary_benchmarks.push(internal_binary_benchmark);
}
}
fn low_level(
&mut self,
internal_group: &mut InternalBinaryBenchmarkGroup,
group: crate::BinaryBenchmarkGroup,
module_path: &str,
) {
let module_path = ModulePath::new(module_path).join(&internal_group.id);
if group.binary_benchmarks.is_empty() {
self.errors.add(Error::new(
&module_path,
"This group needs at least one benchmark",
));
return;
}
let mut binary_benchmark_ids = HashSet::<BenchmarkId>::new();
for binary_benchmark in group.binary_benchmarks {
let module_path = module_path.join(&binary_benchmark.id.to_string());
if let Err(message) = binary_benchmark.id.validate() {
self.errors.add(Error::new(&module_path, &message));
continue;
}
if !binary_benchmark_ids.insert(binary_benchmark.id.clone()) {
self.errors
.add(Error::new(&module_path, "Duplicate binary benchmark id"));
continue;
}
let mut internal_binary_benchmark = InternalBinaryBenchmark {
benches: vec![],
config: binary_benchmark.config,
};
let mut bench_ids = HashSet::<BenchmarkId>::new();
if binary_benchmark.benches.is_empty() {
self.errors.add(Error::new(
&module_path,
"This binary benchmark needs at least one bench",
));
}
for bench in binary_benchmark.benches {
let module_path = module_path.join(&bench.id.to_string());
match bench.commands.as_slice() {
[] => {
self.errors.add(Error::new(&module_path, "Missing command"));
}
[command] => {
if let Err(message) = bench.id.validate() {
self.errors.add(Error::new(&module_path, &message));
}
if !bench_ids.insert(bench.id.clone()) {
self.errors.add(Error::new(
&module_path,
&format!("Duplicate id: '{}'", bench.id),
));
}
let internal_bench = InternalBinaryBenchmarkBench {
id: Some(bench.id.into()),
args: None,
function_name: binary_benchmark.id.clone().into(),
command: command.into(),
config: bench.config.clone(),
has_setup: bench.setup.is_some() || binary_benchmark.setup.is_some(),
has_teardown: bench.teardown.is_some()
|| binary_benchmark.teardown.is_some(),
};
internal_binary_benchmark.benches.push(internal_bench);
}
commands => {
for (index, command) in commands.iter().enumerate() {
let bench_id: BenchmarkId = format!("{}_{}", bench.id, index).into();
if let Err(message) = bench_id.validate() {
self.errors.add(Error::new(&module_path, &message));
continue;
}
if !bench_ids.insert(bench_id.clone()) {
self.errors.add(Error::new(
&module_path,
&format!("Duplicate id: '{bench_id}'"),
));
continue;
}
let internal_bench = InternalBinaryBenchmarkBench {
id: Some(bench_id.into()),
args: None,
function_name: binary_benchmark.id.to_string(),
command: command.into(),
config: bench.config.clone(),
has_setup: bench.setup.is_some()
|| binary_benchmark.setup.is_some(),
has_teardown: bench.teardown.is_some()
|| binary_benchmark.teardown.is_some(),
};
internal_binary_benchmark.benches.push(internal_bench);
}
}
}
}
internal_group
.binary_benchmarks
.push(internal_binary_benchmark);
}
}
pub fn build(self) -> Result<InternalBinaryBenchmarkGroups, Errors> {
if self.errors.is_empty() {
Ok(self.groups)
} else {
Err(self.errors)
}
}
}