iai_callgrind/
bin_bench.rs

1use std::ffi::{OsStr, OsString};
2use std::fmt::Display;
3use std::fs::File;
4use std::io::{BufRead, BufReader};
5use std::net::SocketAddr;
6use std::path::{Path, PathBuf};
7use std::time::Duration;
8
9use derive_more::AsRef;
10use iai_callgrind_macros::IntoInner;
11use iai_callgrind_runner::api::RawArgs;
12
13use crate::{internal, DelayKind, Stdin, Stdio};
14
15/// [low level api](`crate::binary_benchmark_group`) only: Create a new benchmark id
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct BenchmarkId(String);
18
19/// The configuration of a binary benchmark
20///
21/// The [`BinaryBenchmarkConfig`] can be specified at multiple levels and configures the benchmarks
22/// at this level. For example a [`BinaryBenchmarkConfig`] at (`main`)[`crate::main`] level
23/// configures all benchmarks. A configuration at [`group`](crate::binary_benchmark_group) level
24/// configures all benchmarks in this group inheriting the configuration of the `main` level and if
25/// not specified otherwise overwrites the values of the `main` configuration if the option is
26/// specified in both [`BinaryBenchmarkConfig`]s. The deeper levels are the
27/// (`#[binary_benchmark] attribute`)[`crate::binary_benchmark`], then `#[bench]` and the
28/// `#[benches]` attribute.
29///
30/// # Examples
31///
32/// ```rust
33/// # use iai_callgrind::binary_benchmark_group;
34/// # binary_benchmark_group!(name = some_group; benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
35/// use iai_callgrind::{BinaryBenchmarkConfig, main};
36///
37/// main!(
38///     config = BinaryBenchmarkConfig::default().callgrind_args(["toggle-collect=something"]);
39///     binary_benchmark_groups = some_group
40/// );
41/// ```
42#[derive(Debug, Default, Clone, IntoInner, AsRef)]
43pub struct BinaryBenchmarkConfig(internal::InternalBinaryBenchmarkConfig);
44
45/// [low level api](`crate::binary_benchmark_group`) only: The top level struct to add binary
46/// benchmarks to
47///
48/// This struct doesn't need to be instantiated by yourself. It is passed as mutable reference to
49/// the expression in `benchmarks`.
50///
51/// ```rust
52/// use iai_callgrind::binary_benchmark_group;
53///
54/// binary_benchmark_group!(
55///     name = my_group;
56///     benchmarks = |_group: &mut BinaryBenchmarkGroup| {
57///         // Access the BinaryBenchmarkGroup with the identifier `group` to add benchmarks to the
58///         // group.
59///         //
60///         // group.binary_benchmark(/* BinaryBenchmark::new(...) */);
61///     }
62/// );
63/// ```
64#[derive(Debug, Default, PartialEq, Clone)]
65pub struct BinaryBenchmarkGroup {
66    /// All [`BinaryBenchmark`]s
67    pub binary_benchmarks: Vec<BinaryBenchmark>,
68}
69
70/// [low level api](`crate::binary_benchmark_group`) only: This struct mirrors the `#[bench]` and
71/// `#[benches]` attribute of a [`crate::binary_benchmark`]
72#[derive(Debug, Clone, PartialEq)]
73pub struct Bench {
74    /// The [`BenchmarkId`] used to uniquely identify this benchmark within a [`BinaryBenchmark`]
75    pub id: BenchmarkId,
76    /// All [`Command`]s
77    pub commands: Vec<Command>,
78    /// An optional [`BinaryBenchmarkConfig`]
79    ///
80    /// This field stores the internal representation of the [`BinaryBenchmarkConfig`]. Use
81    /// `BinaryBenchmarkConfig::into` to generate the internal configuration from a
82    /// [`BinaryBenchmarkConfig`]
83    pub config: Option<internal::InternalBinaryBenchmarkConfig>,
84    /// The `setup` function to be executed before the [`Command`] is executed
85    pub setup: Option<fn()>,
86    /// The `teardown` function to be executed after the [`Command`] is executed
87    pub teardown: Option<fn()>,
88}
89
90/// [low level api](`crate::binary_benchmark_group`) only: Mirror the [`crate::binary_benchmark`]
91/// attribute
92///
93/// A `BinaryBenchmark` can be created in two ways. Either with [`BinaryBenchmark::new`]. Or via the
94/// [`crate::binary_benchmark_attribute`] macro used with a function annotated with the
95/// [`crate::binary_benchmark`] attribute. So, you can start with the high-level api using the
96/// attribute and then go on in the low-level api.
97///
98/// # Examples
99///
100/// For examples using [`BinaryBenchmark::new`], see there. Here's an example using the
101/// [`crate::binary_benchmark_attribute`]
102///
103/// ```rust
104/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
105/// use iai_callgrind::{
106///     binary_benchmark, binary_benchmark_group, binary_benchmark_attribute, Bench
107/// };
108///
109/// #[binary_benchmark]
110/// #[bench::foo("foo")]
111/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
112///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
113///         .arg(arg)
114///         .build()
115/// }
116///
117/// binary_benchmark_group!(
118///     name = my_group;
119///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
120///         let mut binary_benchmark = binary_benchmark_attribute!(bench_binary);
121///
122///         // Continue and add another `Bench` to the `BinaryBenchmark`
123///         binary_benchmark.bench(Bench::new("bar")
124///             .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
125///                 .arg("bar")
126///             )
127///         );
128///
129///         // Finally, add the `BinaryBenchmark` to the group
130///         group
131///             .binary_benchmark(binary_benchmark);
132///     }
133/// );
134/// # fn main() {}
135/// ```
136#[derive(Debug, Clone, PartialEq)]
137pub struct BinaryBenchmark {
138    /// An id which has to be unique within the same [`BinaryBenchmarkGroup`]
139    ///
140    /// In the high-level api this is the name of the function which is annotated by
141    /// [`crate::binary_benchmark`]
142    pub id: BenchmarkId,
143    /// An optional [`BinaryBenchmarkConfig`] which is applied to all [`Command`]s within this
144    /// [`BinaryBenchmark`]
145    pub config: Option<internal::InternalBinaryBenchmarkConfig>,
146    /// All [`Bench`]es which were added to this [`BinaryBenchmark`]
147    pub benches: Vec<Bench>,
148    /// The default `setup` function for all [`Bench`]es within this [`BinaryBenchmark`]. It can be
149    /// overwritten in a [`Bench`]
150    pub setup: Option<fn()>,
151    /// The default `teardown` function for all [`Bench`]es within this [`BinaryBenchmark`]. It can
152    /// be overwritten in a [`Bench`]
153    pub teardown: Option<fn()>,
154}
155
156/// Provide the [`Command`] to be benchmarked
157///
158/// `Command` is a builder for the binary which is going to be benchmarked providing fine-grained
159/// control over how the `Command` for the valgrind benchmark should be executed.
160///
161/// The default configuration is created with [`Command::new`] providing a path to an executable.
162/// Adding a crate's binary is usually done with `env!("CARGO_BIN_EXE_<name>")` where `<name>` is
163/// the name of the binary. The builder methods allow the configuration to be changed prior to
164/// [`Command::build`]. The [`Command`] can be reused to build multiple processes.
165///
166/// # Examples
167///
168/// Suppose your crate's binary is called `my-echo`:
169///
170/// ```rust
171/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
172/// use iai_callgrind::Command;
173/// let command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
174/// ```
175///
176/// However, an iai-callgrind benchmark is not limited to a crate's binaries, it can be any
177/// executable in the `$PATH`, or an absolute path to a binary installed on your system. The
178/// following will create a `Command` for the system's `echo` from the `$PATH`:
179///
180/// ```rust
181/// use iai_callgrind::Command;
182/// let command = Command::new("echo");
183/// ```
184#[derive(Debug, Default, Clone, PartialEq, IntoInner, AsRef)]
185pub struct Command(internal::InternalCommand);
186
187/// Provide the [`crate::Delay`] to specify the event for [`crate::Command`] execution start.
188///
189/// The default configuration is created with [`Delay::new`] providing a [`crate::DelayKind`] to
190/// specify the event type and parameters for the `Delay`.
191///
192/// Additionally, the `Delay` can be created using `from*()` methods.
193/// - [`Delay::from(duration)`](Delay::from)
194/// - [`Delay::from_tcp_socket(addr)`](Delay::from_tcp_socket)
195/// - [`Delay::from_udp_request(addr, request)`](Delay::from_udp_request)
196/// - [`Delay::from_path(path)`](Delay::from_path)
197///
198/// # Examples
199///
200/// Suppose your command needs to start 60 seconds after the benchmark started:
201///
202/// ```rust
203/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
204/// use std::time::Duration;
205///
206/// use iai_callgrind::{Command, Delay, DelayKind};
207///
208/// let command = Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Delay::new(
209///     DelayKind::DurationElapse(Duration::from_secs(60)),
210/// ));
211///
212/// let command_from =
213///     Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Delay::from(Duration::from_secs(60)));
214///
215/// let command_duration =
216///     Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Duration::from_secs(60));
217/// ```
218///
219/// However, an iai-callgrind [`Delay`] is not limited to a duration, it can be any
220/// path creation event, a successful TCP connect or as well a received UDP response.
221///
222/// ```rust
223/// use iai_callgrind::{Command, Delay, DelayKind};
224///
225/// let command = Command::new("echo").delay(Delay::new(DelayKind::PathExists(
226///     "/your/path/to/wait/for".into(),
227/// )));
228///
229/// let command_from = Command::new("echo").delay(Delay::from_path("/your/path/to/wait/for"));
230/// ```
231///
232/// ```rust
233/// use std::net::SocketAddr;
234/// use std::time::Duration;
235///
236/// use iai_callgrind::{Command, Delay, DelayKind};
237///
238/// let command = Command::new("echo").delay(
239///     Delay::new(DelayKind::TcpConnect(
240///         "127.0.0.1:31000".parse::<SocketAddr>().unwrap(),
241///     ))
242///     .timeout(Duration::from_secs(3))
243///     .poll(Duration::from_millis(50)),
244/// );
245///
246/// let command_from = Command::new("echo").delay(
247///     Delay::from_tcp_socket("127.0.0.1:31000".parse::<SocketAddr>().unwrap())
248///         .timeout(Duration::from_secs(3))
249///         .poll(Duration::from_millis(50)),
250/// );
251/// ```
252///
253/// ```rust
254/// use std::net::SocketAddr;
255/// use std::time::Duration;
256///
257/// use iai_callgrind::{Command, Delay, DelayKind};
258///
259/// let command = Command::new("echo").delay(
260///     Delay::new(DelayKind::UdpResponse(
261///         "127.0.0.1:34000".parse::<SocketAddr>().unwrap(),
262///         vec![1],
263///     ))
264///     .timeout(Duration::from_secs(3))
265///     .poll(Duration::from_millis(50)),
266/// );
267///
268/// let command_from = Command::new("echo").delay(
269///     Delay::from_udp_request("127.0.0.1:34000".parse::<SocketAddr>().unwrap(), vec![1])
270///         .timeout(Duration::from_secs(3))
271///         .poll(Duration::from_millis(50)),
272/// );
273/// ```
274#[derive(Debug, Default, Clone, PartialEq, IntoInner, AsRef)]
275pub struct Delay(internal::InternalDelay);
276
277/// Set the expected exit status of a binary benchmark
278///
279/// Per default, the benchmarked binary is expected to succeed, but if a benchmark is expected to
280/// fail, setting this option is required.
281///
282/// # Examples
283///
284/// ```rust
285/// # use iai_callgrind::{binary_benchmark_group};
286/// # binary_benchmark_group!(
287/// #    name = my_group;
288/// #    benchmarks = |group: &mut BinaryBenchmarkGroup| {});
289/// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
290///
291/// # fn main() {
292/// main!(
293///     config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Code(1));
294///     binary_benchmark_groups = my_group
295/// );
296/// # }
297/// ```
298#[derive(Debug, Clone, Copy)]
299pub enum ExitWith {
300    /// Exit with success is similar to `ExitCode(0)`
301    Success,
302    /// Exit with failure is similar to setting the `ExitCode` to something different from `0`
303    /// without having to rely on a specific exit code
304    Failure,
305    /// The exact `ExitCode` of the benchmark run
306    Code(i32),
307}
308
309/// The `Sandbox` in which the `setup`, `teardown` and the [`Command`] are run
310///
311/// The `Sandbox` is a temporary directory which is created before the execution of the
312/// [`setup`](`crate::binary_benchmark`) and deleted after the
313/// [`teardown`](`crate::binary_benchmark`). `setup`, the [`Command`] and `teardown` are executed
314/// inside this temporary directory.
315///
316/// # Background and reasons for using a `Sandbox`
317///
318/// A [`Sandbox`] can help mitigating differences in benchmark results on different machines. As
319/// long as `$TMP_DIR` is unset or set to `/tmp`, the temporary directory has a constant length on
320/// unix machines (except android which uses `/data/local/tmp`). The directory itself
321/// is created with a constant length but random name like `/tmp/.a23sr8fk`. It is not implausible
322/// that an executable has different event counts just because the directory it is executed in has a
323/// different length. For example, if a member of your project has set up the project in
324/// `/home/bob/workspace/our-project` running the benchmarks in this directory, and the ci runs the
325/// benchmarks in `/runner/our-project`, the event counts might differ. If possible, the benchmarks
326/// should be run in an as constant as possible environment. Clearing the environment variables is
327/// also such a counter-measure.
328///
329/// Other reasons for using a `Sandbox` are convenience, such as if you're creating files during
330/// `setup` and the [`Command`] run and don't want to delete all the files manually. Or, more
331/// importantly, if the [`Command`] is destructive and deletes files, it is usually safer to execute
332/// such a [`Command`] in a temporary directory where it cannot do any harm to your or others file
333/// systems during the benchmark runs.
334///
335/// # Sandbox cleanup
336///
337/// The changes the `setup` makes in this directory persist until the `teardown` has finished. So,
338/// the [`Command`] can for example pick up any files created by the `setup` method. If run in a
339/// `Sandbox`, the `teardown` usually doesn't have to delete any files, because the whole
340/// directory is deleted after its usage. There is an exception to the rule. If any of the files
341/// inside the directory is not removable, for example because the permissions of a file don't allow
342/// the file to be deleted, then the whole directory persists. You can use the `teardown` to reset
343/// all permission bits to be readable and writable, so the cleanup can succeed.
344///
345/// To simply copy fixtures or whole directories into the `Sandbox` use [`Sandbox::fixtures`].
346#[derive(Debug, Clone, IntoInner, AsRef)]
347pub struct Sandbox(internal::InternalSandbox);
348
349impl Bench {
350    /// Create a new `Bench` with a unique [`BenchmarkId`]
351    ///
352    /// If the provided [`BenchmarkId`] is invalid, `iai-callgrind` exits with an error.
353    ///
354    /// # Scope of uniqueness of the [`BenchmarkId`]
355    ///
356    /// The id needs to be unique within the same [`BinaryBenchmark`]
357    ///
358    /// # Examples
359    ///
360    /// The [`BenchmarkId`] can be created from any &str-like
361    ///
362    /// ```
363    /// use iai_callgrind::Bench;
364    ///
365    /// let bench = Bench::new("my_unique_id");
366    /// ```
367    ///
368    /// but you can also provide the [`BenchmarkId`]
369    ///
370    /// ```
371    /// use iai_callgrind::{Bench, BenchmarkId};
372    ///
373    /// let bench = Bench::new(BenchmarkId::new("my_unique_id"));
374    /// ```
375    pub fn new<T>(id: T) -> Self
376    where
377        T: Into<BenchmarkId>,
378    {
379        Self {
380            id: id.into(),
381            config: None,
382            commands: vec![],
383            setup: None,
384            teardown: None,
385        }
386    }
387
388    /// Add a [`BinaryBenchmarkConfig`] for this `Bench`
389    ///
390    /// A `Bench` without a `BinaryBenchmarkConfig` behaves like having specified the default
391    /// [`BinaryBenchmarkConfig`]. This [`BinaryBenchmarkConfig`] overwrites the values of a
392    /// [`BinaryBenchmarkConfig`] specified at a higher level. See there for more details.
393    ///
394    /// # Examples
395    ///
396    /// ```
397    /// use iai_callgrind::{Bench, BinaryBenchmarkConfig};
398    ///
399    /// let bench = Bench::new("some_id").config(BinaryBenchmarkConfig::default().env("FOO", "BAR"));
400    /// ```
401    pub fn config<T>(&mut self, config: T) -> &mut Self
402    where
403        T: Into<internal::InternalBinaryBenchmarkConfig>,
404    {
405        self.config = Some(config.into());
406        self
407    }
408
409    /// Add a [`Command`] to this `Bench`
410    ///
411    /// A `Bench` with multiple `Commands` behaves exactly as the
412    /// [`#[benches]`](crate::binary_benchmark) attribute
413    ///
414    /// # Errors
415    ///
416    /// It is an error if a `Bench` does not contain any `Commands`, so this method or
417    /// [`Bench::commands`] has to be called at least once.
418    ///
419    /// # Examples
420    ///
421    /// Suppose the crate's binary is called `my-echo`:
422    ///
423    /// ```rust
424    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
425    /// use iai_callgrind::{Bench, Command};
426    ///
427    /// let bench = Bench::new("some_id")
428    ///     .command(Command::new(env!("CARGO_BIN_EXE_my-echo")))
429    ///     .clone();
430    ///
431    /// assert_eq!(bench.commands.len(), 1);
432    /// ```
433    pub fn command<T>(&mut self, command: T) -> &mut Self
434    where
435        T: Into<Command>,
436    {
437        self.commands.push(command.into());
438        self
439    }
440
441    /// Add multiple [`Command`]s to this `Bench`
442    ///
443    /// See also [`Bench::command`].
444    ///
445    /// # Examples
446    ///
447    /// Suppose the crate's binary is called `my-echo`
448    ///
449    /// ```rust
450    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
451    /// use iai_callgrind::{Bench, Command};
452    ///
453    /// let mut command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
454    ///
455    /// let echo_foo = command.clone().arg("foo").build();
456    /// let echo_bar = command.arg("bar").build();
457    ///
458    /// let mut bench = Bench::new("some_id");
459    /// bench.commands([echo_foo, echo_bar]).clone();
460    ///
461    /// assert_eq!(bench.commands.len(), 2);
462    /// ```
463    pub fn commands<I, T>(&mut self, commands: T) -> &mut Self
464    where
465        I: Into<Command>,
466        T: IntoIterator<Item = I>,
467    {
468        self.commands.extend(commands.into_iter().map(Into::into));
469        self
470    }
471
472    /// Add a `setup` function to be executed before the [`Command`] is executed
473    ///
474    /// This `setup` function overwrites the `setup` function of [`BinaryBenchmark`]. In the
475    /// presence of a [`Sandbox`], this function is executed in the sandbox.
476    ///
477    /// # Examples
478    ///
479    /// ```rust
480    /// use iai_callgrind::Bench;
481    ///
482    /// fn my_setup() {
483    ///     println!("Place everything in this function you want to be executed prior to the Command");
484    /// }
485    ///
486    /// let mut bench = Bench::new("some_id");
487    /// bench.setup(my_setup);
488    ///
489    /// assert!(bench.setup.is_some())
490    /// ```
491    ///
492    /// Overwrite the setup function from a [`BinaryBenchmark`]
493    ///
494    /// ```rust
495    /// use iai_callgrind::{Bench, BinaryBenchmark};
496    /// fn binary_benchmark_setup() {
497    ///     println!("setup in BinaryBenchmark")
498    /// }
499    ///
500    /// fn bench_setup() {
501    ///     println!("setup in Bench")
502    /// }
503    ///
504    /// BinaryBenchmark::new("bench_binary")
505    ///     .setup(binary_benchmark_setup)
506    ///     .bench(Bench::new("some_id").setup(bench_setup));
507    /// ```
508    pub fn setup(&mut self, setup: fn()) -> &mut Self {
509        self.setup = Some(setup);
510        self
511    }
512
513    /// Add a `teardown` function to be executed after the [`Command`] is executed
514    ///
515    /// This `teardown` function overwrites the `teardown` function of [`BinaryBenchmark`]. In the
516    /// presence of a [`Sandbox`], this function is executed in the sandbox.
517    ///
518    /// # Examples
519    ///
520    /// ```rust
521    /// use iai_callgrind::Bench;
522    ///
523    /// fn my_teardown() {
524    ///     println!(
525    ///         "Place everything in this function you want to be executed after the execution of the \
526    ///          Command"
527    ///     );
528    /// }
529    ///
530    /// let mut bench = Bench::new("some_id");
531    /// bench.teardown(my_teardown);
532    ///
533    /// assert!(bench.teardown.is_some())
534    /// ```
535    ///
536    /// Overwrite the teardown function from a [`BinaryBenchmark`]
537    ///
538    /// ```rust
539    /// use iai_callgrind::{Bench, BinaryBenchmark};
540    /// fn binary_benchmark_teardown() {
541    ///     println!("teardown in BinaryBenchmark")
542    /// }
543    ///
544    /// fn bench_teardown() {
545    ///     println!("teardown in Bench")
546    /// }
547    ///
548    /// BinaryBenchmark::new("bench_binary")
549    ///     .teardown(binary_benchmark_teardown)
550    ///     .bench(Bench::new("some_id").teardown(bench_teardown));
551    /// ```
552    pub fn teardown(&mut self, teardown: fn()) -> &mut Self {
553        self.teardown = Some(teardown);
554        self
555    }
556
557    /// Add each line of a file as [`Command`] to this [`Bench`] using a `generator` function.
558    ///
559    /// This method mirrors the `file` parameter of the `#[benches]` attribute as far as possible.
560    /// In the low-level api you can achieve the same or more quickly yourself and this method
561    /// exists for the sake of completeness (and convenience).
562    ///
563    /// The file has to exist at the time you're using this method and the file has to be encoded in
564    /// UTF-8. The `generator` function tells us how to convert each line of the file into a
565    /// [`Command`].
566    ///
567    /// # Notable differences to high-level api
568    ///
569    /// If the file path in the high-level api is relative we interpret the path relative to the
570    /// workspace root (and make it absolute). In this method we use the path AS IS.
571    ///
572    /// # Errors
573    ///
574    /// If the file is empty, cannot be opened for reading or a line in the file cannot be converted
575    /// to a String. Also, the error from the `generator` is propagated. The errors containing the
576    /// line number use a 0-indexed line number.
577    ///
578    /// # Examples
579    ///
580    /// Suppose your cargo's binary is named `my-echo` and you want to convert a file with inputs
581    /// `benches/inputs` into commands and each line is the only argument for your `my-echo` binary:
582    ///
583    /// ```rust
584    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
585    /// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench};
586    ///
587    /// binary_benchmark_group!(
588    ///     name = my_group;
589    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
590    ///         group.binary_benchmark(BinaryBenchmark::new("some_id")
591    ///             .bench(Bench::new("bench_id")
592    ///                 .file("benches/inputs", |line| {
593    ///                     Ok(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
594    ///                         .arg(line)
595    ///                         .build())
596    ///                 }).unwrap()
597    ///             )
598    ///         )
599    ///     }
600    /// );
601    /// # fn main() {}
602    /// ```
603    pub fn file<T>(
604        &mut self,
605        path: T,
606        generator: fn(String) -> Result<Command, String>,
607    ) -> Result<&mut Self, String>
608    where
609        T: AsRef<Path>,
610    {
611        let path = path.as_ref();
612        let file = File::open(path).map_err(|error| {
613            format!(
614                "{}: Error opening file '{}': {error}",
615                self.id,
616                path.display(),
617            )
618        })?;
619
620        let reader = BufReader::new(file);
621        let mut has_lines = false;
622        for (index, line) in reader.lines().enumerate() {
623            has_lines = true;
624
625            let line = line.map_err(|error| {
626                format!(
627                    "{}: Error reading line {index} in file '{}': {error}",
628                    self.id,
629                    path.display()
630                )
631            })?;
632
633            let command = generator(line).map_err(|error| {
634                format!(
635                    "{}: Error generating command from line {index} in file '{}': {error}",
636                    self.id,
637                    path.display()
638                )
639            })?;
640            self.commands.push(command);
641        }
642
643        if !has_lines {
644            return Err(format!("{}: Empty file '{}'", self.id, path.display()));
645        }
646        Ok(self)
647    }
648}
649
650impl From<&mut Bench> for Bench {
651    fn from(value: &mut Bench) -> Self {
652        value.clone()
653    }
654}
655
656impl From<&Bench> for Bench {
657    fn from(value: &Bench) -> Self {
658        value.clone()
659    }
660}
661
662impl BenchmarkId {
663    /// Convenience method to create a `BenchmarkId` with a parameter in the [low level
664    /// api](crate::binary_benchmark_group)
665    ///
666    /// The `parameter` is simply appended to the `id` with an underscore, so
667    /// `BenchmarkId::with_parameter("some", 1)` is equivalent to `BenchmarkId::new("some_1")`
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// use iai_callgrind::BenchmarkId;
673    ///
674    /// let new_id = BenchmarkId::new("prefix_1");
675    /// let with_parameter = BenchmarkId::with_parameter("prefix", 1);
676    /// assert_eq!(new_id, with_parameter);
677    /// ```
678    ///
679    /// ```rust
680    /// # use iai_callgrind::main;
681    /// use iai_callgrind::{binary_benchmark_group,BenchmarkId, BinaryBenchmark, Bench, Command};
682    /// use std::ffi::OsStr;
683    ///
684    /// binary_benchmark_group!(
685    ///     name = low_level_group;
686    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
687    ///         let mut binary_benchmark = BinaryBenchmark::new("some_id");
688    ///         for arg in 0..10 {
689    ///             let id = BenchmarkId::with_parameter("prefix", arg);
690    ///             binary_benchmark.bench(
691    ///                 Bench::new(id)
692    ///                     .command(
693    ///                         Command::new("echo").arg(arg.to_string()).build()
694    ///                     )
695    ///             );
696    ///         }
697    ///         group.binary_benchmark(binary_benchmark);
698    ///     }
699    /// );
700    /// # fn main() {
701    /// # main!(binary_benchmark_groups = low_level_group);
702    /// # }
703    /// ```
704    pub fn with_parameter<T, P>(id: T, parameter: P) -> Self
705    where
706        T: AsRef<str>,
707        P: Display,
708    {
709        Self(format!("{}_{parameter}", id.as_ref()))
710    }
711
712    /// Create a new `BenchmarkId`
713    ///
714    /// `BenchmarkId`s can be created from any string-like input. See [`BenchmarkId::validate`] for
715    /// ids which are considered valid.
716    ///
717    /// # Examples
718    ///
719    /// ```rust
720    /// use iai_callgrind::BenchmarkId;
721    ///
722    /// let id = BenchmarkId::new("my_id");
723    ///
724    /// assert!(id.validate().is_ok());
725    /// ```
726    pub fn new<T>(id: T) -> Self
727    where
728        T: Into<String>,
729    {
730        Self(id.into())
731    }
732
733    /// Returns ok if this [`BenchmarkId`] is valid
734    ///
735    /// An id should be short, descriptive besides being unique. The requirements for the uniqueness
736    /// differ for the structs where a `BenchmarkId` is used and is further described there.
737    ///
738    /// We use a minimal subset of rust's identifiers. A valid `BenchmarkId` starts with an ascii
739    /// alphabetic letter `[a-zA-Z]` or underscore `[_]`. All following characters can be an ascii
740    /// alphabetic letter, underscore or a digit `[0-9]`. At least one valid character must be
741    /// present.
742    ///
743    /// The `BenchmarkId` is used by `iai-callgrind` as file and directory name for the output files
744    /// of the benchmarks. Therefore, the limit for an id is chosen to be 120 bytes. This is a
745    /// calculation with some headroom. There are file systems which do not even allow 255 bytes. If
746    /// you're working on such a peculiar file system, you have to restrict your ids to even fewer
747    /// bytes which is `floor(MAX_LENGTH/2) - 1`. Leaving the maximum bytes aside, the best IDs are
748    /// simple, short and descriptive.
749    ///
750    /// Usually, it is not necessary to call this function, since we already check the validity of
751    /// benchmark ids prior to the execution of the benchmark runner. But if your ids come from an
752    /// untrusted source you can use this method for more immediate feedback.
753    ///
754    /// # Errors
755    ///
756    /// This function will return an error describing the source of the error if the id is invalid
757    ///
758    /// # Examples
759    ///
760    /// ```rust
761    /// use iai_callgrind::BenchmarkId;
762    ///
763    /// assert!(BenchmarkId::new("").validate().is_err());
764    /// assert!(BenchmarkId::new("0a").validate().is_err());
765    /// assert!(BenchmarkId::new("id with spaces").validate().is_err());
766    /// assert!(BenchmarkId::new("path/to").validate().is_err());
767    /// assert!(BenchmarkId::new("no::module::too").validate().is_err());
768    ///
769    /// assert!(BenchmarkId::new("_").validate().is_ok());
770    /// assert!(BenchmarkId::new("abc").validate().is_ok());
771    /// assert!(BenchmarkId::new("a9").validate().is_ok());
772    /// assert!(BenchmarkId::new("z_").validate().is_ok());
773    /// assert!(BenchmarkId::new("some_id").validate().is_ok());
774    /// ```
775    #[allow(clippy::missing_panics_doc)]
776    pub fn validate(&self) -> Result<(), String> {
777        const MAX_LENGTH_ID: usize = 255;
778        if self.0.is_empty() {
779            return Err("Invalid id: Cannot be empty".to_owned());
780        }
781
782        let mut bytes = self.0.bytes();
783        // This unwrap is safe, since we just checked that the string is not empty
784        let first = bytes.next().unwrap();
785
786        if first.is_ascii_alphabetic() || first == b'_' {
787            for (index, byte) in (1..).zip(bytes) {
788                if index > MAX_LENGTH_ID {
789                    return Err(format!(
790                        "Invalid id '{}': Maximum length of {MAX_LENGTH_ID} bytes reached",
791                        &self.0,
792                    ));
793                }
794                if byte.is_ascii() {
795                    if !(byte.is_ascii_alphanumeric() || byte == b'_') {
796                        return Err(format!(
797                            "Invalid id '{}' at position {index}: Invalid character '{}'",
798                            &self.0,
799                            char::from(byte)
800                        ));
801                    }
802                } else {
803                    return Err(format!(
804                        "Invalid id '{}' at position {index}: Encountered non-ascii character",
805                        &self.0
806                    ));
807                }
808            }
809        } else if first.is_ascii() {
810            return Err(format!(
811                "Invalid id '{}': As first character is '{}' not allowed",
812                &self.0,
813                char::from(first)
814            ));
815        } else {
816            return Err(format!(
817                "Invalid id '{}': Encountered non-ascii character as first character",
818                &self.0
819            ));
820        }
821        Ok(())
822    }
823}
824
825impl Display for BenchmarkId {
826    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
827        f.write_str(&self.0)
828    }
829}
830
831impl From<BenchmarkId> for String {
832    fn from(value: BenchmarkId) -> Self {
833        value.0
834    }
835}
836
837impl<T> From<T> for BenchmarkId
838where
839    T: AsRef<str>,
840{
841    fn from(value: T) -> Self {
842        Self(value.as_ref().to_owned())
843    }
844}
845
846impl BinaryBenchmark {
847    /// Create a new `BinaryBenchmark`
848    ///
849    /// A `BinaryBenchmark` is the equivalent of the
850    /// [`#[binary_benchmark]`](`crate::binary_benchmark`) attribute in the low-level api and needs
851    /// a [`BenchmarkId`]. In the high-level api the id is derived from the function name.
852    ///
853    /// The [`BenchmarkId`] is used together with the id of each [`Bench`] to create a directory
854    /// name. This usually limits the combined length of the ids to `255 - 1` but can be less
855    /// depending on the file system. See [`BenchmarkId`] for more details
856    ///
857    /// # Scope of uniqueness of the [`BenchmarkId`]
858    ///
859    /// The id needs to be unique within the same [`crate::binary_benchmark_group`]. It is
860    /// recommended to use an id which is unique within the whole benchmark file, although doing
861    /// otherwise does currently not incur any negative consequence.
862    ///
863    /// # Examples
864    ///
865    /// ```rust
866    /// use iai_callgrind::{BenchmarkId, BinaryBenchmark};
867    ///
868    /// let binary_benchmark = BinaryBenchmark::new("some_id");
869    /// assert_eq!(binary_benchmark.id, BenchmarkId::new("some_id"));
870    /// ```
871    pub fn new<T>(id: T) -> Self
872    where
873        T: Into<BenchmarkId>,
874    {
875        Self {
876            id: id.into(),
877            config: None,
878            benches: vec![],
879            setup: None,
880            teardown: None,
881        }
882    }
883
884    /// Add a [`BinaryBenchmarkConfig`] to this `BinaryBenchmark`
885    ///
886    /// # Examples
887    ///
888    /// ```rust
889    /// use iai_callgrind::{BinaryBenchmark, BinaryBenchmarkConfig};
890    ///
891    /// let binary_benchmark = BinaryBenchmark::new("some_id")
892    ///     .config(BinaryBenchmarkConfig::default().env("FOO", "BAR"))
893    ///     .clone();
894    ///
895    /// assert_eq!(
896    ///     binary_benchmark.config,
897    ///     Some(BinaryBenchmarkConfig::default().env("FOO", "BAR").into())
898    /// );
899    /// ```
900    pub fn config<T>(&mut self, config: T) -> &mut Self
901    where
902        T: Into<internal::InternalBinaryBenchmarkConfig>,
903    {
904        self.config = Some(config.into());
905        self
906    }
907
908    /// Add a [`Bench`] to this `BinaryBenchmark`
909    ///
910    /// Adding a [`Bench`] which doesn't contain a [`Command`] is an error.
911    ///
912    /// # Examples
913    ///
914    /// ```rust
915    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
916    /// use iai_callgrind::{Bench, BinaryBenchmark, Command};
917    ///
918    /// // Each `Bench` needs at least one `Command`!
919    /// let mut bench = Bench::new("some_id");
920    /// bench.command(Command::new(env!("CARGO_BIN_EXE_my-echo")));
921    ///
922    /// let binary_benchmark = BinaryBenchmark::new("bench_binary")
923    ///     .bench(bench.clone())
924    ///     .clone();
925    ///
926    /// assert_eq!(binary_benchmark.benches[0], bench);
927    /// ```
928    pub fn bench<T>(&mut self, bench: T) -> &mut Self
929    where
930        T: Into<Bench>,
931    {
932        self.benches.push(bench.into());
933        self
934    }
935
936    /// Add a `setup` function to this `BinaryBenchmark`
937    ///
938    /// This `setup` function is used in all [`Bench`]es of this `BinaryBenchmark` if not overridden
939    /// by the `Bench`.
940    ///
941    /// # Examples
942    ///
943    /// ```rust
944    /// use iai_callgrind::Bench;
945    /// fn my_setup() {
946    ///     println!(
947    ///         "Place everything in this function you want to be executed before the execution of \
948    ///          the Command"
949    ///     );
950    /// }
951    ///
952    /// let bench = Bench::new("some_id").setup(my_setup).clone();
953    ///
954    /// assert!(bench.setup.is_some())
955    /// ```
956    ///
957    /// Overwrite the setup function from this `BinaryBenchmark` in a [`Bench`]
958    ///
959    /// ```rust
960    /// use iai_callgrind::{Bench, BinaryBenchmark};
961    /// fn binary_benchmark_setup() {
962    ///     println!("setup in BinaryBenchmark")
963    /// }
964    ///
965    /// fn bench_setup() {
966    ///     println!("setup in Bench")
967    /// }
968    ///
969    /// BinaryBenchmark::new("bench_binary")
970    ///     .setup(binary_benchmark_setup)
971    ///     .bench(Bench::new("some_id").setup(bench_setup));
972    /// ```
973    pub fn setup(&mut self, setup: fn()) -> &mut Self {
974        self.setup = Some(setup);
975        self
976    }
977
978    /// Add a `teardown` function to this `BinaryBenchmark`
979    ///
980    /// This `teardown` function is used in all [`Bench`]es of this `BinaryBenchmark` if not
981    /// overridden by the `Bench`.
982    ///
983    /// # Examples
984    ///
985    /// ```rust
986    /// use iai_callgrind::Bench;
987    /// fn my_teardown() {
988    ///     println!(
989    ///         "Place everything in this function you want to be executed after the execution of the \
990    ///          Command"
991    ///     );
992    /// }
993    ///
994    /// let bench = Bench::new("some_id").teardown(my_teardown).clone();
995    ///
996    /// assert!(bench.teardown.is_some())
997    /// ```
998    ///
999    /// Overwrite the teardown function from this `BinaryBenchmark` in a [`Bench`]
1000    ///
1001    /// ```rust
1002    /// use iai_callgrind::{Bench, BinaryBenchmark};
1003    /// fn binary_benchmark_teardown() {
1004    ///     println!("teardown in BinaryBenchmark")
1005    /// }
1006    ///
1007    /// fn bench_teardown() {
1008    ///     println!("teardown in Bench")
1009    /// }
1010    ///
1011    /// BinaryBenchmark::new("bench_binary")
1012    ///     .teardown(binary_benchmark_teardown)
1013    ///     .bench(Bench::new("some_id").teardown(bench_teardown));
1014    /// ```
1015    pub fn teardown(&mut self, teardown: fn()) -> &mut Self {
1016        self.teardown = Some(teardown);
1017        self
1018    }
1019}
1020
1021impl From<&mut BinaryBenchmark> for BinaryBenchmark {
1022    fn from(value: &mut BinaryBenchmark) -> Self {
1023        value.clone()
1024    }
1025}
1026
1027impl From<&BinaryBenchmark> for BinaryBenchmark {
1028    fn from(value: &BinaryBenchmark) -> Self {
1029        value.clone()
1030    }
1031}
1032
1033impl BinaryBenchmarkConfig {
1034    /// Pass arguments to valgrind's callgrind
1035    ///
1036    /// It's not needed to pass the arguments with flags. Instead of `--collect-atstart=no` simply
1037    /// write `collect-atstart=no`.
1038    ///
1039    /// See also [Callgrind Command-line
1040    /// Options](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options) for a full
1041    /// overview of possible arguments.
1042    ///
1043    /// # Examples
1044    ///
1045    /// ```rust
1046    /// use iai_callgrind::BinaryBenchmarkConfig;
1047    ///
1048    /// BinaryBenchmarkConfig::default()
1049    ///     .callgrind_args(["collect-atstart=no", "toggle-collect=some::path"]);
1050    /// ```
1051    #[deprecated = "Please use callgrind_args instead"]
1052    pub fn raw_callgrind_args<I, T>(&mut self, args: T) -> &mut Self
1053    where
1054        I: AsRef<str>,
1055        T: IntoIterator<Item = I>,
1056    {
1057        self.0.callgrind_args.extend_ignore_flag(args);
1058        self
1059    }
1060
1061    /// Create a new `BinaryBenchmarkConfig` with initial callgrind arguments
1062    ///
1063    /// See also [`BinaryBenchmarkConfig::callgrind_args`].
1064    ///
1065    /// # Examples
1066    ///
1067    /// ```rust
1068    /// # use iai_callgrind::{binary_benchmark, binary_benchmark_group};
1069    /// # #[binary_benchmark]
1070    /// # fn some_func() -> iai_callgrind::Command { iai_callgrind::Command::new("/some/path") }
1071    /// # binary_benchmark_group!(name = some_group; benchmarks = some_func);
1072    /// # fn main() {
1073    /// use iai_callgrind::{BinaryBenchmarkConfig, main};
1074    ///
1075    /// main!(
1076    ///     config =
1077    ///         BinaryBenchmarkConfig::with_callgrind_args(["toggle-collect=something"]);
1078    ///     binary_benchmark_groups = some_group
1079    /// );
1080    /// # }
1081    /// ```
1082    pub fn with_callgrind_args<I, T>(args: T) -> Self
1083    where
1084        I: AsRef<str>,
1085        T: IntoIterator<Item = I>,
1086    {
1087        Self(internal::InternalBinaryBenchmarkConfig {
1088            callgrind_args: RawArgs::from_iter(args),
1089            ..Default::default()
1090        })
1091    }
1092
1093    /// Pass arguments to valgrind's callgrind
1094    ///
1095    /// It's not needed to pass the arguments with flags. Instead of `--collect-atstart=no` simply
1096    /// write `collect-atstart=no`.
1097    ///
1098    /// See also [Callgrind Command-line
1099    /// Options](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options) for a full
1100    /// overview of possible arguments.
1101    ///
1102    /// # Examples
1103    ///
1104    /// ```rust
1105    /// use iai_callgrind::BinaryBenchmarkConfig;
1106    ///
1107    /// BinaryBenchmarkConfig::default()
1108    ///     .callgrind_args(["collect-atstart=no", "toggle-collect=some::path"]);
1109    /// ```
1110    pub fn callgrind_args<I, T>(&mut self, args: T) -> &mut Self
1111    where
1112        I: AsRef<str>,
1113        T: IntoIterator<Item = I>,
1114    {
1115        self.0.callgrind_args.extend_ignore_flag(args);
1116        self
1117    }
1118
1119    /// Pass valgrind arguments to all tools
1120    ///
1121    /// Only core [valgrind
1122    /// arguments](https://valgrind.org/docs/manual/manual-core.html#manual-core.options) are
1123    /// allowed.
1124    ///
1125    /// These arguments can be overwritten by tool specific arguments for example with
1126    /// [`BinaryBenchmarkConfig::callgrind_args`] or [`crate::Tool::args`].
1127    ///
1128    /// # Examples
1129    ///
1130    /// Specify `--trace-children=no` for all configured tools (including callgrind):
1131    ///
1132    /// ```rust
1133    /// # use iai_callgrind::{binary_benchmark_group};
1134    /// # binary_benchmark_group!(
1135    /// #    name = my_group;
1136    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1137    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1138    ///
1139    /// # fn main() {
1140    /// main!(
1141    ///     config = BinaryBenchmarkConfig::default()
1142    ///         .valgrind_args(["--trace-children=no"])
1143    ///         .tool(Tool::new(ValgrindTool::DHAT));
1144    ///     binary_benchmark_groups = my_group
1145    /// );
1146    /// # }
1147    /// ```
1148    ///
1149    /// Overwrite the valgrind argument `--num-callers=25` for `DHAT` with `--num-callers=30`:
1150    ///
1151    /// ```rust
1152    /// # use iai_callgrind::{binary_benchmark_group};
1153    /// # binary_benchmark_group!(
1154    /// #    name = my_group;
1155    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1156    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1157    ///
1158    /// # fn main() {
1159    /// main!(
1160    ///     config = BinaryBenchmarkConfig::default()
1161    ///         .valgrind_args(["--num-callers=25"])
1162    ///         .tool(Tool::new(ValgrindTool::DHAT)
1163    ///             .args(["--num-callers=30"])
1164    ///         );
1165    ///     binary_benchmark_groups = my_group
1166    /// );
1167    /// # }
1168    /// ```
1169    pub fn valgrind_args<I, T>(&mut self, args: T) -> &mut Self
1170    where
1171        I: AsRef<str>,
1172        T: IntoIterator<Item = I>,
1173    {
1174        self.0.valgrind_args.extend_ignore_flag(args);
1175        self
1176    }
1177
1178    /// Add an environment variable to the [`Command`]
1179    ///
1180    /// These environment variables are available independently of the setting of
1181    /// [`BinaryBenchmarkConfig::env_clear`].
1182    ///
1183    /// # Examples
1184    ///
1185    /// An example for a custom environment variable "FOO=BAR":
1186    ///
1187    /// ```rust
1188    /// # use iai_callgrind::{binary_benchmark_group};
1189    /// # binary_benchmark_group!(
1190    /// #    name = my_group;
1191    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1192    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1193    ///
1194    /// # fn main() {
1195    /// main!(
1196    ///     config = BinaryBenchmarkConfig::default().env("FOO", "BAR");
1197    ///     binary_benchmark_groups = my_group
1198    /// );
1199    /// # }
1200    /// ```
1201    pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
1202    where
1203        K: Into<OsString>,
1204        V: Into<OsString>,
1205    {
1206        self.0.envs.push((key.into(), Some(value.into())));
1207        self
1208    }
1209
1210    /// Add multiple environment variables to the [`Command`]
1211    ///
1212    /// # Examples
1213    ///
1214    /// ```rust
1215    /// # use iai_callgrind::{binary_benchmark_group};
1216    /// # binary_benchmark_group!(
1217    /// #    name = my_group;
1218    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1219    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1220    ///
1221    /// # fn main() {
1222    /// main!(
1223    ///     config = BinaryBenchmarkConfig::default().envs([("FOO", "BAR"),("BAR", "BAZ")]);
1224    ///     binary_benchmark_groups = my_group
1225    /// );
1226    /// # }
1227    /// ```
1228    pub fn envs<K, V, T>(&mut self, envs: T) -> &mut Self
1229    where
1230        K: Into<OsString>,
1231        V: Into<OsString>,
1232        T: IntoIterator<Item = (K, V)>,
1233    {
1234        self.0
1235            .envs
1236            .extend(envs.into_iter().map(|(k, v)| (k.into(), Some(v.into()))));
1237        self
1238    }
1239
1240    /// Specify a pass-through environment variable
1241    ///
1242    /// Usually, the environment variables before running a binary benchmark are cleared
1243    /// but specifying pass-through variables makes this environment variable available to
1244    /// the benchmark as it actually appeared in the root environment.
1245    ///
1246    /// Pass-through environment variables are ignored if they don't exist in the root
1247    /// environment.
1248    ///
1249    /// # Examples
1250    ///
1251    /// Here, we chose to pass through the original value of the `HOME` variable:
1252    ///
1253    /// ```rust
1254    /// # use iai_callgrind::{binary_benchmark_group};
1255    /// # binary_benchmark_group!(
1256    /// #    name = my_group;
1257    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1258    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1259    ///
1260    /// # fn main() {
1261    /// main!(
1262    ///     config = BinaryBenchmarkConfig::default().pass_through_env("HOME");
1263    ///     binary_benchmark_groups = my_group
1264    /// );
1265    /// # }
1266    /// ```
1267    pub fn pass_through_env<K>(&mut self, key: K) -> &mut Self
1268    where
1269        K: Into<OsString>,
1270    {
1271        self.0.envs.push((key.into(), None));
1272        self
1273    }
1274
1275    /// Specify multiple pass-through environment variables
1276    ///
1277    /// See also [`crate::BinaryBenchmarkConfig::pass_through_env`].
1278    ///
1279    /// # Examples
1280    ///
1281    /// ```rust
1282    /// # use iai_callgrind::binary_benchmark_group;
1283    /// # binary_benchmark_group!(
1284    /// #    name = my_group;
1285    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1286    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1287    ///
1288    /// # fn main() {
1289    /// main!(
1290    ///     config = BinaryBenchmarkConfig::default().pass_through_envs(["HOME", "USER"]);
1291    ///     binary_benchmark_groups = my_group
1292    /// );
1293    /// # }
1294    /// ```
1295    pub fn pass_through_envs<K, T>(&mut self, envs: T) -> &mut Self
1296    where
1297        K: Into<OsString>,
1298        T: IntoIterator<Item = K>,
1299    {
1300        self.0
1301            .envs
1302            .extend(envs.into_iter().map(|k| (k.into(), None)));
1303        self
1304    }
1305
1306    /// If false, don't clear the environment variables before running the benchmark (Default: true)
1307    ///
1308    /// # Examples
1309    ///
1310    /// ```rust
1311    /// # use iai_callgrind::{binary_benchmark_group};
1312    /// # binary_benchmark_group!(
1313    /// #    name = my_group;
1314    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1315    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1316    ///
1317    /// # fn main() {
1318    /// main!(
1319    ///     config = BinaryBenchmarkConfig::default().env_clear(false);
1320    ///     binary_benchmark_groups = my_group
1321    /// );
1322    /// # }
1323    /// ```
1324    pub fn env_clear(&mut self, value: bool) -> &mut Self {
1325        self.0.env_clear = Some(value);
1326        self
1327    }
1328
1329    /// Set the directory of the benchmarked binary (Default: Unchanged)
1330    ///
1331    /// Unchanged means, in the case of running with the sandbox enabled, the root of the sandbox.
1332    /// In the case of running without sandboxing enabled, this'll be the directory which `cargo
1333    /// bench` sets. If running the benchmark within the sandbox, and the path is relative then this
1334    /// new directory must be contained in the sandbox.
1335    ///
1336    /// # Examples
1337    ///
1338    /// ```rust
1339    /// # use iai_callgrind::{binary_benchmark_group};
1340    /// # binary_benchmark_group!(
1341    /// #    name = my_group;
1342    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1343    /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1344    ///
1345    /// # fn main() {
1346    /// main!(
1347    ///     config = BinaryBenchmarkConfig::default().current_dir("/tmp");
1348    ///     binary_benchmark_groups = my_group
1349    /// );
1350    /// # }
1351    /// ```
1352    ///
1353    /// and the following will change the current directory to `fixtures` assuming it is
1354    /// contained in the root of the sandbox
1355    ///
1356    /// ```rust
1357    /// # use iai_callgrind::{binary_benchmark_group};
1358    /// # binary_benchmark_group!(
1359    /// #    name = my_group;
1360    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1361    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Sandbox};
1362    ///
1363    /// # fn main() {
1364    /// main!(
1365    ///     config = BinaryBenchmarkConfig::default()
1366    ///         .sandbox(Sandbox::new(true))
1367    ///         .current_dir("fixtures");
1368    ///     binary_benchmark_groups = my_group
1369    /// );
1370    /// # }
1371    /// ```
1372    pub fn current_dir<T>(&mut self, value: T) -> &mut Self
1373    where
1374        T: Into<PathBuf>,
1375    {
1376        self.0.current_dir = Some(value.into());
1377        self
1378    }
1379
1380    /// Set the expected exit status [`ExitWith`] of a benchmarked binary
1381    ///
1382    /// Per default, the benchmarked binary is expected to succeed which is the equivalent of
1383    /// [`ExitWith::Success`]. But, if a benchmark is expected to fail, setting this option is
1384    /// required.
1385    ///
1386    /// # Examples
1387    ///
1388    /// If the benchmark is expected to fail with a specific exit code, for example `100`:
1389    ///
1390    /// ```rust
1391    /// # use iai_callgrind::{binary_benchmark_group};
1392    /// # binary_benchmark_group!(
1393    /// #    name = my_group;
1394    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1395    /// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
1396    ///
1397    /// # fn main() {
1398    /// main!(
1399    ///     config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Code(100));
1400    ///     binary_benchmark_groups = my_group
1401    /// );
1402    /// # }
1403    /// ```
1404    ///
1405    /// If a benchmark is expected to fail, but the exit code doesn't matter:
1406    ///
1407    /// ```rust
1408    /// # use iai_callgrind::{binary_benchmark_group};
1409    /// # binary_benchmark_group!(
1410    /// #    name = my_group;
1411    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1412    /// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
1413    ///
1414    /// # fn main() {
1415    /// main!(
1416    ///     config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Failure);
1417    ///     binary_benchmark_groups = my_group
1418    /// );
1419    /// # }
1420    /// ```
1421    pub fn exit_with<T>(&mut self, value: T) -> &mut Self
1422    where
1423        T: Into<internal::InternalExitWith>,
1424    {
1425        self.0.exit_with = Some(value.into());
1426        self
1427    }
1428
1429    /// Option to produce flamegraphs from callgrind output using the [`crate::FlamegraphConfig`]
1430    ///
1431    /// # Examples
1432    ///
1433    /// ```rust
1434    /// # use iai_callgrind::{binary_benchmark_group};
1435    /// # binary_benchmark_group!(
1436    /// #    name = my_group;
1437    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1438    /// use iai_callgrind::{main, BinaryBenchmarkConfig, FlamegraphConfig };
1439    ///
1440    /// # fn main() {
1441    /// main!(
1442    ///     config = BinaryBenchmarkConfig::default().flamegraph(FlamegraphConfig::default());
1443    ///     binary_benchmark_groups = my_group
1444    /// );
1445    /// # }
1446    /// ```
1447    pub fn flamegraph<T>(&mut self, config: T) -> &mut Self
1448    where
1449        T: Into<internal::InternalFlamegraphConfig>,
1450    {
1451        self.0.flamegraph_config = Some(config.into());
1452        self
1453    }
1454
1455    /// Enable performance regression checks with a [`crate::RegressionConfig`]
1456    ///
1457    /// # Examples
1458    ///
1459    /// ```rust
1460    /// # use iai_callgrind::{binary_benchmark_group};
1461    /// # binary_benchmark_group!(
1462    /// #    name = my_group;
1463    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1464    /// use iai_callgrind::{main, BinaryBenchmarkConfig, RegressionConfig};
1465    ///
1466    /// # fn main() {
1467    /// main!(
1468    ///     config = BinaryBenchmarkConfig::default().regression(RegressionConfig::default());
1469    ///     binary_benchmark_groups = my_group
1470    /// );
1471    /// # }
1472    /// ```
1473    pub fn regression<T>(&mut self, config: T) -> &mut Self
1474    where
1475        T: Into<internal::InternalRegressionConfig>,
1476    {
1477        self.0.regression_config = Some(config.into());
1478        self
1479    }
1480
1481    /// Add a configuration to run a valgrind [`crate::Tool`] in addition to callgrind
1482    ///
1483    /// # Examples
1484    ///
1485    /// ```rust
1486    /// # use iai_callgrind::{binary_benchmark_group};
1487    /// # binary_benchmark_group!(
1488    /// #    name = my_group;
1489    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1490    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1491    ///
1492    /// # fn main() {
1493    /// main!(
1494    ///     config = BinaryBenchmarkConfig::default()
1495    ///         .tool(
1496    ///             Tool::new(ValgrindTool::DHAT)
1497    ///         );
1498    ///     binary_benchmark_groups = my_group
1499    /// );
1500    /// # }
1501    /// ```
1502    pub fn tool<T>(&mut self, tool: T) -> &mut Self
1503    where
1504        T: Into<internal::InternalTool>,
1505    {
1506        self.0.tools.update(tool.into());
1507        self
1508    }
1509
1510    /// Add multiple configurations to run valgrind [`crate::Tool`]s in addition to callgrind
1511    ///
1512    /// # Examples
1513    ///
1514    /// ```rust
1515    /// # use iai_callgrind::{binary_benchmark_group};
1516    /// # binary_benchmark_group!(
1517    /// #    name = my_group;
1518    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1519    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1520    ///
1521    /// # fn main() {
1522    /// main!(
1523    ///     config = BinaryBenchmarkConfig::default()
1524    ///         .tools(
1525    ///             [
1526    ///                 Tool::new(ValgrindTool::DHAT),
1527    ///                 Tool::new(ValgrindTool::Massif)
1528    ///             ]
1529    ///         );
1530    ///     binary_benchmark_groups = my_group
1531    /// );
1532    /// # }
1533    /// ```
1534    pub fn tools<I, T>(&mut self, tools: T) -> &mut Self
1535    where
1536        I: Into<internal::InternalTool>,
1537        T: IntoIterator<Item = I>,
1538    {
1539        self.0.tools.update_all(tools.into_iter().map(Into::into));
1540        self
1541    }
1542
1543    /// Override previously defined configurations of valgrind [`crate::Tool`]s
1544    ///
1545    /// See also [`crate::LibraryBenchmarkConfig::tool_override`] for more details.
1546    ///
1547    /// # Example
1548    ///
1549    /// The following will run `DHAT` and `Massif` (and the default callgrind) for all benchmarks in
1550    /// `main!` besides for `foo` which will just run `Memcheck` (and callgrind).
1551    ///
1552    /// ```rust
1553    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1554    /// use iai_callgrind::{
1555    ///     binary_benchmark, binary_benchmark_group, BinaryBenchmarkConfig, main, Tool,
1556    ///     ValgrindTool
1557    /// };
1558    ///
1559    /// #[binary_benchmark]
1560    /// #[bench::some(
1561    ///     config = BinaryBenchmarkConfig::default()
1562    ///         .tool_override(Tool::new(ValgrindTool::Memcheck))
1563    /// )]
1564    /// fn bench_binary() -> iai_callgrind::Command {
1565    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1566    /// }
1567    ///
1568    /// binary_benchmark_group!(
1569    ///     name = my_group;
1570    ///     benchmarks = bench_binary
1571    /// );
1572    ///
1573    /// # fn main() {
1574    /// main!(
1575    ///     config = BinaryBenchmarkConfig::default()
1576    ///         .tools(
1577    ///             [
1578    ///                 Tool::new(ValgrindTool::DHAT),
1579    ///                 Tool::new(ValgrindTool::Massif)
1580    ///             ]
1581    ///         );
1582    ///     binary_benchmark_groups = my_group
1583    /// );
1584    /// # }
1585    /// ```
1586    pub fn tool_override<T>(&mut self, tool: T) -> &mut Self
1587    where
1588        T: Into<internal::InternalTool>,
1589    {
1590        self.0
1591            .tools_override
1592            .get_or_insert(internal::InternalTools::default())
1593            .update(tool.into());
1594        self
1595    }
1596
1597    /// Override previously defined configurations of valgrind [`crate::Tool`]s
1598    ///
1599    /// See also [`crate::LibraryBenchmarkConfig::tool_override`] for more details.
1600    ///
1601    /// # Example
1602    ///
1603    /// The following will run `DHAT` (and the default callgrind) for all benchmarks in
1604    /// `main!` besides for `foo` which will run `Massif` and `Memcheck` (and callgrind).
1605    ///
1606    /// ```rust
1607    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1608    /// use iai_callgrind::{
1609    ///     binary_benchmark, binary_benchmark_group, BinaryBenchmarkConfig, main, Tool,
1610    ///     ValgrindTool
1611    /// };
1612    ///
1613    /// #[binary_benchmark]
1614    /// #[bench::some(
1615    ///     config = BinaryBenchmarkConfig::default()
1616    ///         .tools_override([
1617    ///             Tool::new(ValgrindTool::Massif),
1618    ///             Tool::new(ValgrindTool::Memcheck),
1619    ///         ])
1620    /// )]
1621    /// fn bench_binary() -> iai_callgrind::Command {
1622    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1623    /// }
1624    ///
1625    /// binary_benchmark_group!(
1626    ///     name = my_group;
1627    ///     benchmarks = bench_binary
1628    /// );
1629    ///
1630    /// # fn main() {
1631    /// main!(
1632    ///     config = BinaryBenchmarkConfig::default()
1633    ///         .tool(
1634    ///             Tool::new(ValgrindTool::DHAT),
1635    ///         );
1636    ///     binary_benchmark_groups = my_group
1637    /// );
1638    /// # }
1639    /// ```
1640    pub fn tools_override<I, T>(&mut self, tools: T) -> &mut Self
1641    where
1642        I: Into<internal::InternalTool>,
1643        T: IntoIterator<Item = I>,
1644    {
1645        self.0
1646            .tools_override
1647            .get_or_insert(internal::InternalTools::default())
1648            .update_all(tools.into_iter().map(Into::into));
1649        self
1650    }
1651
1652    /// Configure benchmarks to run in a [`Sandbox`] (Default: false)
1653    ///
1654    /// If specified, we create a temporary directory in which the `setup` and `teardown` functions
1655    /// of the `#[binary_benchmark]` (`#[bench]`, `#[benches]`) and the [`Command`] itself are run.
1656    ///
1657    /// A good reason for using a temporary directory as workspace is, that the length of the path
1658    /// where a benchmark is executed may have an influence on the benchmark results. For example,
1659    /// running the benchmark in your repository `/home/me/my/repository` and someone else's
1660    /// repository located under `/home/someone/else/repository` may produce different results only
1661    /// because the length of the first path is shorter. To run benchmarks as deterministic as
1662    /// possible across different systems, the length of the path should be the same wherever the
1663    /// benchmark is executed. This crate ensures this property by using the tempfile crate which
1664    /// creates the temporary directory in `/tmp` with a random name of fixed length like
1665    /// `/tmp/.tmp12345678`. This ensures that the length of the directory will be the same on all
1666    /// unix hosts where the benchmarks are run.
1667    ///
1668    /// # Examples
1669    ///
1670    /// ```rust
1671    /// # use iai_callgrind::{binary_benchmark_group};
1672    /// # binary_benchmark_group!(
1673    /// #    name = my_group;
1674    /// #    benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1675    /// use iai_callgrind::{main, BinaryBenchmarkConfig, Sandbox};
1676    ///
1677    /// # fn main() {
1678    /// main!(
1679    ///     config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true));
1680    ///     binary_benchmark_groups = my_group
1681    /// );
1682    /// # }
1683    /// ```
1684    pub fn sandbox<T>(&mut self, sandbox: T) -> &mut Self
1685    where
1686        T: Into<internal::InternalSandbox>,
1687    {
1688        self.0.sandbox = Some(sandbox.into());
1689        self
1690    }
1691
1692    /// Configure the [`crate::OutputFormat`] of the terminal output of Iai-Callgrind
1693    ///
1694    /// # Examples
1695    ///
1696    /// ```rust
1697    /// use iai_callgrind::{main, BinaryBenchmarkConfig, OutputFormat};
1698    /// # use iai_callgrind::{binary_benchmark, binary_benchmark_group};
1699    /// # #[binary_benchmark]
1700    /// # fn some_func() -> iai_callgrind::Command { iai_callgrind::Command::new("some/path") }
1701    /// # binary_benchmark_group!(
1702    /// #    name = some_group;
1703    /// #    benchmarks = some_func
1704    /// # );
1705    /// # fn main() {
1706    /// main!(
1707    ///     config = BinaryBenchmarkConfig::default()
1708    ///         .output_format(OutputFormat::default()
1709    ///             .truncate_description(Some(200))
1710    ///         );
1711    ///     binary_benchmark_groups = some_group
1712    /// );
1713    /// # }
1714    pub fn output_format<T>(&mut self, output_format: T) -> &mut Self
1715    where
1716        T: Into<internal::InternalOutputFormat>,
1717    {
1718        self.0.output_format = Some(output_format.into());
1719        self
1720    }
1721
1722    /// Execute the `setup` in parallel to the [`Command`].
1723    ///
1724    /// See also [`Command::setup_parallel`]
1725    ///
1726    /// # Examples
1727    ///
1728    /// ```rust
1729    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1730    /// use std::time::Duration;
1731    /// use std::net::{SocketAddr, TcpListener};
1732    /// use std::thread;
1733    /// use iai_callgrind::{
1734    ///     binary_benchmark_group, binary_benchmark, main, BinaryBenchmarkConfig, Command,
1735    ///     Delay, DelayKind
1736    /// };
1737    ///
1738    /// fn setup_tcp_server() {
1739    ///     thread::sleep(Duration::from_millis(300));
1740    ///     let _listener = TcpListener::bind("127.0.0.1:31000".parse::<SocketAddr>().unwrap()).unwrap();
1741    ///     thread::sleep(Duration::from_secs(1));
1742    /// }
1743    ///
1744    /// #[binary_benchmark]
1745    /// #[bench::delay(
1746    ///     setup = setup_tcp_server(),
1747    ///     config = BinaryBenchmarkConfig::default()
1748    ///         .setup_parallel(true)
1749    /// )]
1750    /// fn bench_binary() -> iai_callgrind::Command {
1751    ///     Command::new(env!("CARGO_BIN_EXE_my-echo"))
1752    ///         .delay(
1753    ///             Delay::new(
1754    ///                 DelayKind::TcpConnect("127.0.0.1:31000".parse::<SocketAddr>().unwrap()))
1755    ///                 .timeout(Duration::from_millis(500))
1756    ///         ).build()
1757    /// }
1758    ///
1759    /// binary_benchmark_group!(name = delay; benchmarks = bench_binary);
1760    /// # fn main() {
1761    /// main!(binary_benchmark_groups = delay);
1762    /// # }
1763    /// ```
1764    pub fn setup_parallel(&mut self, setup_parallel: bool) -> &mut Self {
1765        self.0.setup_parallel = Some(setup_parallel);
1766        self
1767    }
1768}
1769
1770impl BinaryBenchmarkGroup {
1771    /// Add a [`BinaryBenchmark`] to this group
1772    ///
1773    /// This can be a binary benchmark created with [`BinaryBenchmark::new`] or a
1774    /// [`crate::binary_benchmark`] attributed function addable with the
1775    /// [`crate::binary_benchmark_attribute`] macro.
1776    ///
1777    /// It is an error to add a [`BinaryBenchmark`] without having added a [`Bench`] to it.
1778    ///
1779    /// # Examples
1780    ///
1781    /// Add a [`BinaryBenchmark`] to this group
1782    ///
1783    /// ```rust
1784    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1785    /// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup};
1786    ///
1787    /// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
1788    ///     group.binary_benchmark(BinaryBenchmark::new("bench_binary")
1789    ///         .bench(Bench::new("foo")
1790    ///             .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
1791    ///                 .arg("foo")
1792    ///             )
1793    ///         )
1794    ///     );
1795    /// }
1796    ///
1797    /// binary_benchmark_group!(
1798    ///     name = my_group;
1799    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
1800    /// );
1801    /// # fn main() {}
1802    /// ```
1803    ///
1804    /// Or, add a `#[binary_benchmark]` annotated function to this group:
1805    ///
1806    /// ```rust
1807    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1808    /// use iai_callgrind::{binary_benchmark, binary_benchmark_group, binary_benchmark_attribute};
1809    ///
1810    /// #[binary_benchmark]
1811    /// #[bench::foo("foo")]
1812    /// fn bench_binary(arg: &str) -> iai_callgrind::Command {
1813    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
1814    ///         .arg(arg)
1815    ///         .build()
1816    /// }
1817    ///
1818    /// binary_benchmark_group!(
1819    ///     name = my_group;
1820    ///     benchmarks = |group: &mut BinaryBenchmarkGroup| {
1821    ///         group
1822    ///             .binary_benchmark(binary_benchmark_attribute!(bench_binary))
1823    ///     }
1824    /// );
1825    /// # fn main() {}
1826    /// ```
1827    pub fn binary_benchmark<T>(&mut self, binary_benchmark: T) -> &mut Self
1828    where
1829        T: Into<BinaryBenchmark>,
1830    {
1831        self.binary_benchmarks.push(binary_benchmark.into());
1832        self
1833    }
1834
1835    /// Add multiple [`BinaryBenchmark`]s at once
1836    pub fn binary_benchmarks<I, T>(&mut self, binary_benchmarks: T) -> &mut Self
1837    where
1838        I: Into<BinaryBenchmark>,
1839        T: IntoIterator<Item = I>,
1840    {
1841        self.binary_benchmarks
1842            .extend(binary_benchmarks.into_iter().map(Into::into));
1843        self
1844    }
1845}
1846
1847impl Command {
1848    /// Create a new [`Command`] which is run under valgrind.
1849    ///
1850    /// Use
1851    /// [`env!("CARGO_BIN_EXE_<name>)`](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates)
1852    /// to provide the path to an executable of your project instead of `target/release/<name>`.
1853    ///
1854    /// This `Command` is a builder for the binary which is going to be benchmarked but is not
1855    /// executed right away. We simply gather all the information to be finally able to execute the
1856    /// command under valgrind, later (after we collected all the commands in this benchmark file).
1857    /// As opposed to [`std::process::Command`], the build is finalized with [`Command::build`].
1858    ///
1859    /// # Relative paths
1860    ///
1861    /// Relative paths are interpreted relative to the current directory and if not running the
1862    /// benchmarks in a [`Sandbox`], depends on where `cargo bench` sets the current directory.
1863    /// Usually, it's best to use [`Path::canonicalize`] to resolve the relative path to a binary in
1864    /// your project's directory. In case you're running the benchmark in a [`Sandbox`], the path is
1865    /// interpreted relative to the root directory of the `Sandbox`. Iai-Callgrind tries to resolve
1866    /// simple names like `Command::new("echo")` searching the `$PATH`. To disambiguate between
1867    /// simple names and relative paths, use `./`. For example `echo` is searched in the `$PATH` and
1868    /// `./echo` is interpreted relative.
1869    ///
1870    /// # Examples
1871    ///
1872    /// Assume the project's binary or one of your project's binaries name is `my-echo`:
1873    ///
1874    /// ```
1875    /// # macro_rules! env { ($m:tt) => {{ "/home/my_project/target/release/my-echo" }} }
1876    /// use iai_callgrind::Command;
1877    ///
1878    /// let command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
1879    /// ```
1880    ///
1881    /// or use your system's echo from the `$PATH` with
1882    ///
1883    /// ```
1884    /// use iai_callgrind::Command;
1885    ///
1886    /// let command = Command::new("echo").arg("foo").build();
1887    /// ```
1888    pub fn new<T>(path: T) -> Self
1889    where
1890        T: AsRef<OsStr>,
1891    {
1892        Self(internal::InternalCommand {
1893            path: PathBuf::from(path.as_ref()),
1894            ..Default::default()
1895        })
1896    }
1897
1898    /// Delay the execution of the [`Command`]
1899    ///
1900    /// This option allows to delay the [`Command`] execution till a certain event has happened.
1901    /// Supported events are:
1902    ///  - Timer expired
1903    ///  - File path exists
1904    ///  - TCP/UDP connect succeeded
1905    ///
1906    /// [`Delay`] can be used in combination with [`Command::setup_parallel`] to wait for an event
1907    /// that is triggered within the `setup()` function. E.g. the setup starts a server that is
1908    /// needed by the [`Command`].
1909    ///
1910    /// # Examples
1911    ///
1912    /// ```rust
1913    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1914    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, main, Command, Delay, DelayKind};
1915    ///
1916    /// fn start_server() {
1917    ///     // action to start the server, creates pid file
1918    ///     std::fs::File::create("/tmp/my-server.pid").unwrap();
1919    /// }
1920    ///
1921    /// #[binary_benchmark]
1922    /// #[bench::server(setup = start_server)]
1923    /// fn bench_binary() -> Command {
1924    ///     Command::new(env!("CARGO_BIN_EXE_my-echo"))
1925    ///         .setup_parallel(true)
1926    ///         .delay(Delay::new(DelayKind::PathExists("/tmp/my-server.pid".into())))
1927    ///         .build()
1928    /// }
1929    ///
1930    /// binary_benchmark_group!(name = my_group; benchmarks = bench_binary);
1931    /// # fn main() {
1932    /// main!(binary_benchmark_groups = my_group);
1933    /// # }
1934    /// ```
1935    pub fn delay<T: Into<Delay>>(&mut self, delay: T) -> &mut Self {
1936        self.0.delay = Some(delay.into().into());
1937        self
1938    }
1939
1940    /// Execute the `setup()` in parallel to the [`Command`].
1941    ///
1942    /// This option changes the execution flow in a way that the [`Command`] is executed parallel
1943    /// to the `setup` instead of waiting for the `setup` to complete.
1944    ///
1945    /// This can be combined with the usage of [`Delay`] to further control the timing when the
1946    /// [`Command`] is executed.
1947    ///
1948    /// # Examples
1949    ///
1950    /// ```rust
1951    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1952    /// use std::time::Duration;
1953    /// use std::net::{SocketAddr, TcpListener};
1954    /// use std::thread;
1955    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, main, Command, Delay, DelayKind};
1956    ///
1957    /// fn setup_tcp_server() {
1958    ///     thread::sleep(Duration::from_millis(300));
1959    ///     let _listener = TcpListener::bind("127.0.0.1:31000".parse::<SocketAddr>().unwrap()).unwrap();
1960    ///     thread::sleep(Duration::from_secs(1));
1961    /// }
1962    ///
1963    /// #[binary_benchmark]
1964    /// #[bench::delay(setup = setup_tcp_server())]
1965    /// fn bench_binary() -> iai_callgrind::Command {
1966    ///     Command::new(env!("CARGO_BIN_EXE_my-echo"))
1967    ///         .setup_parallel(true)
1968    ///         .delay(
1969    ///             Delay::new(
1970    ///                 DelayKind::TcpConnect("127.0.0.1:31000".parse::<SocketAddr>().unwrap()))
1971    ///                 .timeout(Duration::from_millis(500))
1972    ///         ).build()
1973    /// }
1974    ///
1975    /// binary_benchmark_group!(name = delay; benchmarks = bench_binary);
1976    /// # fn main() {
1977    /// main!(binary_benchmark_groups = delay);
1978    /// # }
1979    /// ```
1980    pub fn setup_parallel(&mut self, setup_parallel: bool) -> &mut Self {
1981        self.0.config.setup_parallel = Some(setup_parallel);
1982        self
1983    }
1984
1985    /// Adds an argument to pass to the [`Command`]
1986    ///
1987    /// This option works exactly the same way as [`std::process::Command::arg`]. To pass multiple
1988    /// arguments see [`Command::args`].
1989    ///
1990    /// # Examples
1991    ///
1992    /// ```rust
1993    /// # use iai_callgrind::main;
1994    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1995    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
1996    ///
1997    /// #[binary_benchmark]
1998    /// fn bench_binary() -> iai_callgrind::Command {
1999    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
2000    ///         .arg("foo")
2001    ///         .build()
2002    /// }
2003    ///
2004    /// binary_benchmark_group!(
2005    ///     name = my_group;
2006    ///     benchmarks = bench_binary
2007    /// );
2008    /// # fn main() {
2009    /// # main!(binary_benchmark_groups = my_group);
2010    /// # }
2011    /// ```
2012    pub fn arg<T>(&mut self, arg: T) -> &mut Self
2013    where
2014        T: Into<OsString>,
2015    {
2016        self.0.args.push(arg.into());
2017        self
2018    }
2019
2020    /// Adds multiple arguments to pass to the [`Command`]
2021    ///
2022    /// This option works exactly the same way as [`std::process::Command::args`].
2023    ///
2024    /// # Examples
2025    ///
2026    /// The following will execute `my-echo foo`.
2027    ///
2028    /// ```rust
2029    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2030    /// # use iai_callgrind::main;
2031    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2032    ///
2033    /// #[binary_benchmark]
2034    /// fn bench_binary() -> iai_callgrind::Command {
2035    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
2036    ///         .arg("foo")
2037    ///         .build()
2038    /// }
2039    ///
2040    /// binary_benchmark_group!(
2041    ///     name = my_group;
2042    ///     benchmarks = bench_binary
2043    /// );
2044    /// # fn main() {
2045    /// # main!(binary_benchmark_groups = my_group);
2046    /// # }
2047    /// ```
2048    pub fn args<I, T>(&mut self, args: T) -> &mut Self
2049    where
2050        I: Into<OsString>,
2051        T: IntoIterator<Item = I>,
2052    {
2053        self.0.args.extend(args.into_iter().map(Into::into));
2054        self
2055    }
2056
2057    /// Configuration for the process's standard input ([`Stdin`])
2058    ///
2059    /// This method takes an [`Stdin`], [`Stdio`] and everything that implements `Into<Stdin>`. The
2060    /// [`Stdin`] enum mirrors most of the possibilities of [`std::process::Stdio`] but also some
2061    /// additional possibilities most notably [`Stdin::Setup`] (see there for more details).
2062    ///
2063    /// Per default, the stdin is not inherited from the parent and any attempt by the child process
2064    /// to read from the stdin stream will result in the stream immediately closing.
2065    ///
2066    /// The options you might be interested in the most are [`Stdin::File`], which mirrors the
2067    /// behaviour of [`std::process::Stdio`] if `Stdio` is a [`std::fs::File`], and
2068    /// [`Stdin::Setup`], which is special to `iai-callgrind` and lets you pipe the output of
2069    /// the `setup` function into the Stdin of this [`Command`]. If you need to delay the `Command`
2070    /// when using [`Stdin::Setup`], you can do so with [`Command::delay`].
2071    ///
2072    /// # Implementation details
2073    ///
2074    /// As the [`Command`] itself is not executed immediately, the [`std::process::Stdio`] is not
2075    /// created either. We only use the information from here to create the [`std::process::Stdio`]
2076    /// later after we collected all commands. Setting the Stdin to `Inherit` is discouraged and
2077    /// won't have the effect you might expect, since the benchmark runner (the parent) uses the
2078    /// Stdin for its own purposes and closes it before this [`Command`] is executed.
2079    ///
2080    /// # Examples
2081    ///
2082    /// Pipe the content of a file (`benches/fixture.txt`) into the stdin of this [`Command`]:
2083    ///
2084    /// ```rust
2085    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2086    /// # use iai_callgrind::main;
2087    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2088    ///
2089    /// #[binary_benchmark]
2090    /// fn bench_binary() -> iai_callgrind::Command {
2091    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2092    ///         .stdin(Stdio::File("benches/fixture.txt".into()))
2093    ///         .build()
2094    /// }
2095    ///
2096    /// binary_benchmark_group!(
2097    ///     name = my_group;
2098    ///     benchmarks = bench_binary
2099    /// );
2100    /// # fn main() {
2101    /// # main!(binary_benchmark_groups = my_group);
2102    /// # }
2103    /// ```
2104    ///
2105    /// Pipe the Stdout of setup into the Stdin of this [`Command`]:
2106    ///
2107    /// ```rust
2108    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2109    /// # use iai_callgrind::main;
2110    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdin, Pipe};
2111    ///
2112    /// fn setup_pipe() {
2113    ///     // All output to Stdout of this function will be piped into the Stdin of `my-exe`
2114    ///     println!("This string will be piped into the stdin of my-exe");
2115    /// }
2116    ///
2117    /// #[binary_benchmark(setup = setup_pipe())]
2118    /// fn bench_binary() -> iai_callgrind::Command {
2119    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2120    ///         .stdin(Stdin::Setup(Pipe::Stdout))
2121    ///         .build()
2122    /// }
2123    ///
2124    /// binary_benchmark_group!(
2125    ///     name = my_group;
2126    ///     benchmarks = bench_binary
2127    /// );
2128    /// # fn main() {
2129    /// # main!(binary_benchmark_groups = my_group);
2130    /// # }
2131    pub fn stdin<T>(&mut self, stdin: T) -> &mut Self
2132    where
2133        T: Into<Stdin>,
2134    {
2135        self.0.stdin = Some(stdin.into());
2136        self
2137    }
2138
2139    /// Configuration for the [`Command`]s standard output (Stdout) handle.
2140    ///
2141    /// The output of benchmark commands and functions are usually captured by the benchmark runner.
2142    /// This can be changed for example with the `--nocapture` option or here. Any option specified
2143    /// here takes precedence over the changes which `--nocapture` makes to the Stdout of the
2144    /// command.
2145    ///
2146    /// # Examples
2147    ///
2148    /// To see the output of this [`Command`] regardless of `--nocapture` in the benchmark output
2149    ///
2150    /// ```rust
2151    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2152    /// # use iai_callgrind::main;
2153    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2154    ///
2155    /// #[binary_benchmark]
2156    /// fn bench_binary() -> iai_callgrind::Command {
2157    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2158    ///         .stdout(Stdio::Inherit)
2159    ///         .build()
2160    /// }
2161    ///
2162    /// binary_benchmark_group!(
2163    ///     name = my_group;
2164    ///     benchmarks = bench_binary
2165    /// );
2166    /// # fn main() {
2167    /// # main!(binary_benchmark_groups = my_group);
2168    /// # }
2169    /// ```
2170    ///
2171    /// To pipe the Stdout into a file `/tmp/benchmark.output`:
2172    ///
2173    /// ```rust
2174    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2175    /// # use iai_callgrind::main;
2176    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2177    /// use std::path::PathBuf;
2178    ///
2179    /// #[binary_benchmark]
2180    /// fn bench_binary() -> iai_callgrind::Command {
2181    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2182    ///         .stdout(Stdio::File("/tmp/benchmark.output".into()))
2183    ///         // or
2184    ///         .stdout(PathBuf::from("/tmp/benchmark.output"))
2185    ///         .build()
2186    /// }
2187    ///
2188    /// binary_benchmark_group!(
2189    ///     name = my_group;
2190    ///     benchmarks = bench_binary
2191    /// );
2192    /// # fn main() {
2193    /// # main!(binary_benchmark_groups = my_group);
2194    /// # }
2195    /// ```
2196    pub fn stdout<T>(&mut self, stdio: T) -> &mut Self
2197    where
2198        T: Into<Stdio>,
2199    {
2200        self.0.stdout = Some(stdio.into());
2201        self
2202    }
2203
2204    /// Configuration for the [`Command`]s standard error output (Stderr) handle.
2205    ///
2206    /// This option is similar to [`Command::stdout`] but configures the Stderr. See there for more
2207    /// details.
2208    ///
2209    /// # Examples
2210    ///
2211    /// To see the error output of this [`Command`] regardless of `--nocapture` in the benchmark
2212    /// output
2213    ///
2214    /// ```rust
2215    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2216    /// # use iai_callgrind::main;
2217    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2218    ///
2219    /// #[binary_benchmark]
2220    /// fn bench_binary() -> iai_callgrind::Command {
2221    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2222    ///         .stderr(Stdio::Inherit)
2223    ///         .build()
2224    /// }
2225    ///
2226    /// binary_benchmark_group!(
2227    ///     name = my_group;
2228    ///     benchmarks = bench_binary
2229    /// );
2230    /// # fn main() {
2231    /// # main!(binary_benchmark_groups = my_group);
2232    /// # }
2233    /// ```
2234    ///
2235    /// To pipe the Stderr into a file `/tmp/benchmark.output`:
2236    ///
2237    /// ```rust
2238    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2239    /// # use iai_callgrind::main;
2240    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2241    /// use std::path::PathBuf;
2242    ///
2243    /// #[binary_benchmark]
2244    /// fn bench_binary() -> iai_callgrind::Command {
2245    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2246    ///         .stderr(Stdio::File("/tmp/benchmark.output".into()))
2247    ///         // or
2248    ///         .stderr(PathBuf::from("/tmp/benchmark.output"))
2249    ///         .build()
2250    /// }
2251    ///
2252    /// binary_benchmark_group!(
2253    ///     name = my_group;
2254    ///     benchmarks = bench_binary
2255    /// );
2256    /// # fn main() {
2257    /// # main!(binary_benchmark_groups = my_group);
2258    /// # }
2259    /// ```
2260    pub fn stderr<T>(&mut self, stdio: T) -> &mut Self
2261    where
2262        T: Into<Stdio>,
2263    {
2264        self.0.stderr = Some(stdio.into());
2265        self
2266    }
2267
2268    /// Add an environment variable available for this [`Command`]
2269    ///
2270    /// These environment variables are available independently of the setting of
2271    /// [`BinaryBenchmarkConfig::env_clear`] and additive to environment variables added with
2272    /// [`BinaryBenchmarkConfig::env`].
2273    ///
2274    /// # Examples
2275    ///
2276    /// An example for a custom environment variable "FOO=BAR":
2277    ///
2278    /// ```rust
2279    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2280    /// # use iai_callgrind::main;
2281    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2282    ///
2283    /// #[binary_benchmark]
2284    /// fn bench_binary() -> iai_callgrind::Command {
2285    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2286    ///         .env("FOO", "BAR")
2287    ///         .build()
2288    /// }
2289    ///
2290    /// binary_benchmark_group!(
2291    ///     name = my_group;
2292    ///     benchmarks = bench_binary
2293    /// );
2294    /// # fn main() {
2295    /// # main!(binary_benchmark_groups = my_group);
2296    /// # }
2297    pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
2298    where
2299        K: Into<OsString>,
2300        V: Into<OsString>,
2301    {
2302        self.0.config.envs.push((key.into(), Some(value.into())));
2303        self
2304    }
2305
2306    /// Add multiple environment variables available for this [`Command`]
2307    ///
2308    /// See [`Command::env`] for more details.
2309    ///
2310    /// # Examples
2311    ///
2312    /// Add the custom environment variables "FOO=BAR" and `BAR=BAZ`:
2313    ///
2314    /// ```rust
2315    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2316    /// # use iai_callgrind::main;
2317    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2318    ///
2319    /// #[binary_benchmark]
2320    /// fn bench_binary() -> iai_callgrind::Command {
2321    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2322    ///         .envs([("FOO", "BAR"), ("BAR", "BAZ")])
2323    ///         .build()
2324    /// }
2325    ///
2326    /// binary_benchmark_group!(
2327    ///     name = my_group;
2328    ///     benchmarks = bench_binary
2329    /// );
2330    /// # fn main() {
2331    /// # main!(binary_benchmark_groups = my_group);
2332    /// # }
2333    pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
2334    where
2335        I: IntoIterator<Item = (K, V)>,
2336        K: Into<OsString>,
2337        V: Into<OsString>,
2338    {
2339        self.0
2340            .config
2341            .envs
2342            .extend(vars.into_iter().map(|(k, v)| (k.into(), Some(v.into()))));
2343        self
2344    }
2345
2346    /// Set the directory of the benchmarked binary (Default: Unchanged)
2347    ///
2348    /// See also [`BinaryBenchmarkConfig::current_dir`]
2349    ///
2350    /// # Examples
2351    ///
2352    /// To set the working directory of your [`Command`] to `/tmp`:
2353    ///
2354    /// ```rust
2355    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2356    /// # use iai_callgrind::main;
2357    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2358    ///
2359    /// #[binary_benchmark]
2360    /// fn bench_binary() -> iai_callgrind::Command {
2361    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2362    ///         .current_dir("/tmp")
2363    ///         .build()
2364    /// }
2365    ///
2366    /// binary_benchmark_group!(
2367    ///     name = my_group;
2368    ///     benchmarks = bench_binary
2369    /// );
2370    /// # fn main() {
2371    /// # main!(binary_benchmark_groups = my_group);
2372    /// # }
2373    /// ```
2374    ///
2375    /// and the following will change the current directory to `fixtures` located in the root of the
2376    /// [`BinaryBenchmarkConfig::sandbox`]
2377    ///
2378    /// ```rust
2379    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2380    /// # use iai_callgrind::main;
2381    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Sandbox, BinaryBenchmarkConfig};
2382    ///
2383    /// fn setup_sandbox() {
2384    ///     std::fs::create_dir("fixtures").unwrap();
2385    /// }
2386    ///
2387    /// #[binary_benchmark(
2388    ///     setup = setup_sandbox(),
2389    ///     config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true))
2390    /// )]
2391    /// fn bench_binary() -> iai_callgrind::Command {
2392    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2393    ///         .current_dir("fixtures")
2394    ///         .build()
2395    /// }
2396    ///
2397    /// binary_benchmark_group!(
2398    ///     name = my_group;
2399    ///     benchmarks = bench_binary
2400    /// );
2401    /// # fn main() {
2402    /// # main!(binary_benchmark_groups = my_group);
2403    /// # }
2404    /// ```
2405    pub fn current_dir<T>(&mut self, value: T) -> &mut Self
2406    where
2407        T: Into<PathBuf>,
2408    {
2409        self.0.config.current_dir = Some(value.into());
2410        self
2411    }
2412
2413    /// Set the expected exit status [`ExitWith`] of this [`Command`]
2414    ///
2415    /// See also [`BinaryBenchmarkConfig::exit_with`]. This setting overwrites the setting of the
2416    /// [`BinaryBenchmarkConfig`].
2417    ///
2418    /// # Examples
2419    ///
2420    /// If the command is expected to exit with a specific code, for example `100`:
2421    ///
2422    /// ```rust
2423    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2424    /// # use iai_callgrind::main;
2425    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, ExitWith};
2426    ///
2427    /// #[binary_benchmark]
2428    /// fn bench_binary() -> iai_callgrind::Command {
2429    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2430    ///         .exit_with(ExitWith::Code(100))
2431    ///         .build()
2432    /// }
2433    ///
2434    /// binary_benchmark_group!(
2435    ///     name = my_group;
2436    ///     benchmarks = bench_binary
2437    /// );
2438    /// # fn main() {
2439    /// # main!(binary_benchmark_groups = my_group);
2440    /// # }
2441    /// ```
2442    ///
2443    /// If a command is expected to fail, but the exit code doesn't matter:
2444    ///
2445    /// ```rust
2446    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2447    /// # use iai_callgrind::main;
2448    /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, ExitWith};
2449    ///
2450    /// #[binary_benchmark]
2451    /// fn bench_binary() -> iai_callgrind::Command {
2452    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2453    ///         .exit_with(ExitWith::Failure)
2454    ///         .build()
2455    /// }
2456    ///
2457    /// binary_benchmark_group!(
2458    ///     name = my_group;
2459    ///     benchmarks = bench_binary
2460    /// );
2461    /// # fn main() {
2462    /// # main!(binary_benchmark_groups = my_group);
2463    /// # }
2464    /// ```
2465    pub fn exit_with(&mut self, exit_with: ExitWith) -> &mut Self {
2466        self.0.config.exit_with = Some(exit_with.into());
2467        self
2468    }
2469
2470    /// Finalize and build this [`Command`]
2471    ///
2472    /// # Examples
2473    ///
2474    /// ```rust
2475    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2476    /// use iai_callgrind::Command;
2477    ///
2478    /// let command: Command = Command::new(env!("CARGO_BIN_EXE_my-exe"))
2479    ///     .arg("foo")
2480    ///     .build();
2481    /// ```
2482    pub fn build(&mut self) -> Self {
2483        self.clone()
2484    }
2485}
2486
2487impl From<&mut Command> for Command {
2488    fn from(value: &mut Command) -> Self {
2489        value.clone()
2490    }
2491}
2492
2493impl From<&Command> for Command {
2494    fn from(value: &Command) -> Self {
2495        value.clone()
2496    }
2497}
2498
2499impl Delay {
2500    /// Instantiate a [`Delay`] which will wait until a path exists ([`Path::exists`]).
2501    ///
2502    /// ```rust
2503    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2504    ///
2505    /// use iai_callgrind::{Command, Delay};
2506    ///
2507    /// let command = Command::new("echo").delay(Delay::from_path("/your/path/to/wait/for"));
2508    /// ```
2509    pub fn from_path<T>(path: T) -> Self
2510    where
2511        T: Into<PathBuf>,
2512    {
2513        Self(internal::InternalDelay {
2514            kind: DelayKind::PathExists(path.into()),
2515            ..Default::default()
2516        })
2517    }
2518
2519    /// Instantiate a [`Delay`] which will wait until successful TCP connect
2520    /// ([`std::net::TcpStream::connect`]).
2521    ///
2522    /// ```rust
2523    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2524    ///
2525    /// use std::net::SocketAddr;
2526    ///
2527    /// use iai_callgrind::{Command, Delay};
2528    ///
2529    /// let command = Command::new("echo").delay(Delay::from_tcp_socket(
2530    ///     "127.0.0.1:31000".parse::<SocketAddr>().unwrap(),
2531    /// ));
2532    /// ```
2533    pub fn from_tcp_socket<T>(addr: T) -> Self
2534    where
2535        T: Into<SocketAddr>,
2536    {
2537        Self(internal::InternalDelay {
2538            kind: DelayKind::TcpConnect(addr.into()),
2539            ..Default::default()
2540        })
2541    }
2542
2543    /// Instantiate a [`Delay`] which will wait until a UDP response is received after
2544    /// sending the UDP request. The poll duration is also used as the reconnect and request resend
2545    /// interval. ([`std::net::UdpSocket::bind`], [`std::net::UdpSocket::connect`],
2546    /// [`std::net::UdpSocket::recv`]).
2547    ///
2548    /// ```rust
2549    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2550    ///
2551    /// use std::net::SocketAddr;
2552    ///
2553    /// use iai_callgrind::{Command, Delay};
2554    ///
2555    /// let command = Command::new("echo").delay(Delay::from_udp_request(
2556    ///     "127.0.0.1:34000".parse::<SocketAddr>().unwrap(),
2557    ///     vec![1],
2558    /// ));
2559    /// ```
2560    pub fn from_udp_request<T, U>(addr: T, req: U) -> Self
2561    where
2562        T: Into<SocketAddr>,
2563        U: Into<Vec<u8>>,
2564    {
2565        Self(internal::InternalDelay {
2566            kind: DelayKind::UdpResponse(addr.into(), req.into()),
2567            ..Default::default()
2568        })
2569    }
2570
2571    /// Instantiate a [`Delay`] waiting until an event has happened.
2572    ///
2573    /// The possible events are defined in [`DelayKind`].
2574    ///
2575    /// ```rust
2576    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2577    ///
2578    /// use std::time::Duration;
2579    ///
2580    /// use iai_callgrind::{Command, Delay, DelayKind};
2581    ///
2582    /// let command = Command::new("echo").delay(Delay::new(DelayKind::DurationElapse(
2583    ///     Duration::from_secs(15),
2584    /// )));
2585    /// ```
2586    pub fn new(kind: DelayKind) -> Self {
2587        Self(internal::InternalDelay {
2588            kind,
2589            ..Default::default()
2590        })
2591    }
2592
2593    /// Update the [`Delay`] poll interval.
2594    ///
2595    /// The poll interval should be considered together with the [`Delay::timeout`], and ideally
2596    /// should have a value of `n * timeout duration`.
2597    ///
2598    /// In case the poll interval is set to a value `>=` timeout duration it is attempted to set
2599    /// the poll interval to a value of `timeout duration - 5ms`.
2600    ///
2601    /// ```rust
2602    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2603    ///
2604    /// use std::net::SocketAddr;
2605    /// use std::time::Duration;
2606    ///
2607    /// use iai_callgrind::{Command, Delay};
2608    ///
2609    /// let command = Command::new("echo").delay(
2610    ///     Delay::from_udp_request("127.0.0.1:34000".parse::<SocketAddr>().unwrap(), vec![1])
2611    ///         .poll(Duration::from_millis(150)),
2612    /// );
2613    /// ```
2614    pub fn poll<T: Into<Duration>>(mut self, duration: T) -> Self {
2615        self.0.poll = Some(duration.into());
2616        self
2617    }
2618
2619    /// Update the [`Delay`] timeout interval.
2620    ///
2621    /// The timeout duration should be considered together with the poll interval. For further
2622    /// details please refer to [`Delay::poll`]. The minimum timeout duration is `10ms`.
2623    ///
2624    /// ```rust
2625    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2626    ///
2627    /// use std::net::SocketAddr;
2628    /// use std::time::Duration;
2629    ///
2630    /// use iai_callgrind::{Command, Delay};
2631    /// let command = Command::new("echo").delay(
2632    ///     Delay::from_tcp_socket("127.0.0.1:31000".parse::<SocketAddr>().unwrap())
2633    ///         .timeout(Duration::from_secs(5)),
2634    /// );
2635    /// ```
2636    pub fn timeout<T: Into<Duration>>(mut self, duration: T) -> Self {
2637        self.0.timeout = Some(duration.into());
2638        self
2639    }
2640}
2641
2642impl<T> From<T> for Delay
2643where
2644    T: Into<Duration>,
2645{
2646    /// Instantiate a [`Delay`] which will wait until the duration has elapsed.
2647    ///
2648    /// ```rust
2649    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2650    ///
2651    /// use std::time::Duration;
2652    ///
2653    /// use iai_callgrind::{Command, Delay};
2654    ///
2655    /// let command = Command::new("echo").delay(Delay::from(Duration::from_secs(10)));
2656    /// ```
2657    fn from(duration: T) -> Self {
2658        Self(internal::InternalDelay {
2659            kind: DelayKind::DurationElapse(duration.into()),
2660            ..Default::default()
2661        })
2662    }
2663}
2664
2665impl From<ExitWith> for internal::InternalExitWith {
2666    fn from(value: ExitWith) -> Self {
2667        match value {
2668            ExitWith::Success => Self::Success,
2669            ExitWith::Failure => Self::Failure,
2670            ExitWith::Code(c) => Self::Code(c),
2671        }
2672    }
2673}
2674
2675impl From<&ExitWith> for internal::InternalExitWith {
2676    fn from(value: &ExitWith) -> Self {
2677        match value {
2678            ExitWith::Success => Self::Success,
2679            ExitWith::Failure => Self::Failure,
2680            ExitWith::Code(c) => Self::Code(*c),
2681        }
2682    }
2683}
2684
2685impl Sandbox {
2686    /// Create a new `Sandbox` builder
2687    ///
2688    /// Per default, a [`Command`] is not run in a `Sandbox` because setting up a `Sandbox` usually
2689    /// involves some user interaction, for example copying fixtures into it with
2690    /// [`Sandbox::fixtures`].
2691    ///
2692    /// The temporary directory is only created immediately before the `setup` and the [`Command`]
2693    /// are executed.
2694    ///
2695    /// # Examples
2696    ///
2697    /// Enable the sandbox for all benchmarks
2698    ///
2699    /// ```rust
2700    /// use iai_callgrind::{BinaryBenchmarkConfig, Sandbox, main};
2701    /// # use iai_callgrind::binary_benchmark_group;
2702    /// # binary_benchmark_group!(name = my_group; benchmarks = |_group| {});
2703    /// # fn main() {
2704    /// main!(
2705    ///     config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true));
2706    ///     binary_benchmark_groups = my_group
2707    /// );
2708    /// # }
2709    /// ```
2710    pub fn new(enabled: bool) -> Self {
2711        Self(internal::InternalSandbox {
2712            enabled: Some(enabled),
2713            ..Default::default()
2714        })
2715    }
2716
2717    /// Specify the directories and/or files you want to copy into the root of the `Sandbox`
2718    ///
2719    /// The paths are interpreted relative to the workspace root as it is reported by `cargo`. In a
2720    /// multi-crate project this is the directory with the top-level `Cargo.toml`. Otherwise, it is
2721    /// simply the directory with your `Cargo.toml` file in it.
2722    ///
2723    /// # Examples
2724    ///
2725    /// Assuming you crate's binary is called `my-foo` taking a file path as the first argument and
2726    /// the fixtures directory is `$WORKSPACE_ROOT/benches/fixtures` containing a fixture
2727    /// `fix_1.txt`:
2728    ///
2729    /// ```rust
2730    /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2731    /// # use iai_callgrind::{binary_benchmark_group, main};
2732    /// use iai_callgrind::{binary_benchmark, BinaryBenchmarkConfig, Sandbox};
2733    ///
2734    /// #[binary_benchmark]
2735    /// #[bench::fix_1(
2736    ///      args = ("fix_1.txt"),
2737    ///      config = BinaryBenchmarkConfig::default()
2738    ///          .sandbox(Sandbox::new(true)
2739    ///              .fixtures(["benches/fixtures/fix_1.txt"])
2740    ///         )
2741    /// )]
2742    /// fn bench_with_fixtures(path: &str) -> iai_callgrind::Command {
2743    ///     iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
2744    ///         .arg(path)
2745    ///         .build()
2746    /// }
2747    /// # binary_benchmark_group!(name = my_group; benchmarks = bench_with_fixtures);
2748    /// # fn main() { main!(binary_benchmark_groups = my_group); }
2749    /// ```
2750    pub fn fixtures<I, T>(&mut self, paths: T) -> &mut Self
2751    where
2752        I: Into<PathBuf>,
2753        T: IntoIterator<Item = I>,
2754    {
2755        self.0.fixtures.extend(paths.into_iter().map(Into::into));
2756        self
2757    }
2758}
2759
2760#[cfg(test)]
2761mod tests {
2762    use rstest::rstest;
2763
2764    use super::*;
2765
2766    #[rstest]
2767    #[case::empty("")]
2768    #[case::simple_invalid("-")]
2769    #[case::when_0_char_minus_1("a\x2f")]
2770    #[case::when_9_char_plus_1("a\x3a")]
2771    #[case::when_big_a_char_minus_1("\x40")]
2772    #[case::when_big_z_char_plus_1("\x5b")]
2773    #[case::when_low_a_char_minus_1("\x60")]
2774    #[case::when_low_z_char_plus_1("\x7b")]
2775    #[case::invalid_2nd("a-")]
2776    #[case::invalid_3rd("ab-")]
2777    #[case::all_invalid("---")]
2778    #[case::number_as_first("0")]
2779    // This would be a valid rust identifier, but we don't allow the whole set
2780    #[case::non_ascii_1st("µ")]
2781    #[case::non_ascii_2nd("aµ")]
2782    #[case::non_ascii_3rd("aaµ")]
2783    #[case::non_ascii_middle("aµa")]
2784    fn test_benchmark_id_validate_when_error(#[case] id: &str) {
2785        let id = BenchmarkId::new(id);
2786        assert!(id.validate().is_err());
2787    }
2788
2789    #[rstest]
2790    #[case::lowercase_a("a")]
2791    #[case::lowercase_b("b")]
2792    #[case::lowercase_m("m")]
2793    #[case::lowercase_y("y")]
2794    #[case::lowercase_z("z")]
2795    #[case::uppercase_a("A")]
2796    #[case::uppercase_b("B")]
2797    #[case::uppercase_n("N")]
2798    #[case::uppercase_y("Y")]
2799    #[case::uppercase_z("Z")]
2800    #[case::zero_2nd("a0")]
2801    #[case::one_2nd("a1")]
2802    #[case::eight_2nd("a8")]
2803    #[case::nine_2nd("a9")]
2804    #[case::number_middle("b4t")]
2805    #[case::underscore("_")]
2806    #[case::only_underscore("___")]
2807    #[case::underscore_last("a_")]
2808    #[case::mixed_all("auAEwer9__2xcd")]
2809    fn test_benchmark_id_validate(#[case] id: &str) {
2810        let id = BenchmarkId::new(id);
2811        assert!(id.validate().is_ok());
2812    }
2813
2814    #[rstest]
2815    #[case::empty("", "Invalid id: Cannot be empty")]
2816    #[case::non_ascii_first(
2817        "µ",
2818        "Invalid id 'µ': Encountered non-ascii character as first character"
2819    )]
2820    #[case::multibyte_middle(
2821        "aµ",
2822        "Invalid id 'aµ' at position 1: Encountered non-ascii character"
2823    )]
2824    #[case::non_ascii_middle("a-", "Invalid id 'a-' at position 1: Invalid character '-'")]
2825    #[case::invalid_first("-", "Invalid id '-': As first character is '-' not allowed")]
2826    fn test_benchmark_id_validate_error_message(#[case] id: &str, #[case] message: &str) {
2827        let id = BenchmarkId::new(id);
2828        assert_eq!(
2829            id.validate()
2830                .expect_err("Validation should return an error"),
2831            message
2832        );
2833    }
2834}