use std::{any::type_name, ops::Index};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BenchSpec<'a> {
pub(crate) timed: &'a str,
pub(crate) global: Option<&'a str>,
pub(crate) sample: Option<&'a str>,
pub(crate) imports: Option<&'a str>,
pub(crate) decls: Option<&'a str>,
}
macro_rules! option_setter {
(
$(#[$meta:meta])*
$lifetime:lifetime, $fn_name:ident, $var_name:ident
) => {
$(#[$meta])*
pub fn $fn_name(mut self, code: &$lifetime str) -> Self {
if self.$var_name.is_some() {
panic!(concat!("`{}.", stringify!($var_name), "` is already defined"), type_name::<Self>());
}
self.$var_name = Some(code);
self
}
};
}
impl<'a> BenchSpec<'a> {
pub fn new(timed_code: &'a str) -> Self {
Self::from(timed_code)
}
option_setter!(
'a, with_global_init, global
);
option_setter!(
'a, with_sample_init, sample
);
option_setter!(
'a, with_declarations, decls
);
option_setter!(
'a, with_imports, imports
);
}
impl<'a> From<&'a str> for BenchSpec<'a> {
fn from(timed_code: &'a str) -> Self {
Self { timed: timed_code, global: None, sample: None, imports: None, decls: None }
}
}
#[doc(hidden)]
impl<'a> Index<&str> for BenchSpec<'a> {
type Output = str;
fn index(&self, index: &str) -> &'a str {
match index {
"timed" => self.timed,
"global" => self.global.unwrap_or_default(),
"sample" => self.sample.unwrap_or_default(),
"declarations" => self.decls.unwrap_or_default(),
"imports" => self.imports.unwrap_or_default(),
_ => unimplemented!("BenchSpec.{index} does not exist"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
const T: &str = "timed";
const G: &str = "global";
const S: &str = "sample";
const GLOBAL: BenchSpec = BenchSpec { timed: T, global: Some(G), sample: None, imports: None, decls: None };
const SAMPLE: BenchSpec = BenchSpec { timed: T, global: None, sample: Some(S), imports: None, decls: None };
const GS: BenchSpec = BenchSpec { timed: T, global: Some(G), sample: Some(S), imports: None, decls: None };
const BASE: BenchSpec = BenchSpec { timed: T, global: None, sample: None, imports: None, decls: None };
#[test]
fn builder() {
assert_eq!(BASE, BenchSpec::new(T));
assert_eq!(GLOBAL, BASE.with_global_init(G));
assert_eq!(SAMPLE, BASE.with_sample_init(S));
assert_eq!(GS, GLOBAL.with_sample_init(S));
assert_eq!(GS, BASE.with_sample_init(S).with_global_init(G));
}
#[test]
fn from_trait() {
assert_eq!(BASE, BenchSpec::from(T));
}
#[test]
#[should_panic(expected = "already defined")]
fn builder_panic_on_duplicate_global() {
let _panics = GLOBAL.with_global_init("");
}
#[test]
#[should_panic(expected = "already defined")]
fn builder_panic_on_duplicate_sample() {
let _panics = SAMPLE.with_sample_init("");
}
}