1use std::{cmp::Ordering, error::Error, str::FromStr, time::Duration};
2
3use crate::util::sort::natural_cmp;
4
5pub mod filter;
6
7/// `Duration` wrapper for parsing seconds from the CLI.
8#[derive(Clone, Copy)]
9pub(crate) struct ParsedSeconds(pub Duration);
10
11impl FromStr for ParsedSeconds {
12 type Err = Box<dyn Error + Send + Sync>;
13
14 fn from_str(s: &str) -> Result<Self, Self::Err> {
15 Ok(Self(Duration::try_from_secs_f64(secs:f64::from_str(s)?)?))
16 }
17}
18
19/// The primary action to perform.
20#[derive(Clone, Copy, Default)]
21pub(crate) enum Action {
22 /// Run benchmark loops.
23 #[default]
24 Bench,
25
26 /// Run benchmarked functions once to ensure they run successfully.
27 Test,
28
29 /// List benchmarks.
30 List,
31
32 /// List benchmarks in the style of `cargo test --list --format terse`.
33 ///
34 /// This only applies when running under `cargo-nextest` (`NEXTEST=1`).
35 ListTerse,
36}
37
38#[allow(dead_code)]
39impl Action {
40 #[inline]
41 pub fn is_bench(&self) -> bool {
42 matches!(self, Self::Bench)
43 }
44
45 #[inline]
46 pub fn is_test(&self) -> bool {
47 matches!(self, Self::Test)
48 }
49
50 #[inline]
51 pub fn is_list(&self) -> bool {
52 matches!(self, Self::List)
53 }
54
55 #[inline]
56 pub fn is_list_terse(&self) -> bool {
57 matches!(self, Self::ListTerse)
58 }
59}
60
61/// How to treat benchmarks based on whether they're marked as `#[ignore]`.
62#[derive(Copy, Clone, Default)]
63pub(crate) enum RunIgnored {
64 /// Skip ignored.
65 #[default]
66 No,
67
68 /// `--include-ignored`.
69 Yes,
70
71 /// `--ignored`.
72 Only,
73}
74
75impl RunIgnored {
76 pub fn run_ignored(self) -> bool {
77 matches!(self, Self::Yes | Self::Only)
78 }
79
80 pub fn run_non_ignored(self) -> bool {
81 matches!(self, Self::Yes | Self::No)
82 }
83
84 pub fn should_run(self, ignored: bool) -> bool {
85 if ignored {
86 self.run_ignored()
87 } else {
88 self.run_non_ignored()
89 }
90 }
91}
92
93/// The attribute to sort benchmarks by.
94#[derive(Clone, Copy, Default)]
95pub(crate) enum SortingAttr {
96 /// Sort by kind, then by name and location.
97 #[default]
98 Kind,
99
100 /// Sort by name, then by location and kind.
101 Name,
102
103 /// Sort by location, then by kind and name.
104 Location,
105}
106
107impl SortingAttr {
108 /// Returns an array containing `self` along with other attributes that
109 /// should break ties if attributes are equal.
110 pub fn with_tie_breakers(self) -> [Self; 3] {
111 use SortingAttr::*;
112
113 match self {
114 Kind => [self, Name, Location],
115 Name => [self, Location, Kind],
116 Location => [self, Kind, Name],
117 }
118 }
119
120 /// Compares benchmark runtime argument names.
121 ///
122 /// This takes `&&str` to handle `SortingAttr::Location` since the strings
123 /// are considered to be within the same `&[&str]`.
124 pub fn cmp_bench_arg_names(self, a: &&str, b: &&str) -> Ordering {
125 for attr in self.with_tie_breakers() {
126 let ordering = match attr {
127 SortingAttr::Kind => Ordering::Equal,
128
129 SortingAttr::Name => 'ordering: {
130 // Compare as integers.
131 match (a.parse::<u128>(), a.parse::<u128>()) {
132 (Ok(a_u128), Ok(b_u128)) => break 'ordering a_u128.cmp(&b_u128),
133
134 (Ok(_), Err(_)) => {
135 if b.parse::<i128>().is_ok() {
136 // a > b, because b is negative.
137 break 'ordering Ordering::Greater;
138 }
139 }
140
141 (Err(_), Ok(_)) => {
142 if a.parse::<i128>().is_ok() {
143 // a < b, because a is negative.
144 break 'ordering Ordering::Less;
145 }
146 }
147
148 (Err(_), Err(_)) => {
149 if let (Ok(a_i128), Ok(b_i128)) = (a.parse::<i128>(), a.parse::<i128>())
150 {
151 break 'ordering a_i128.cmp(&b_i128);
152 }
153 }
154 }
155
156 // Compare as floats.
157 if let (Ok(a), Ok(b)) = (a.parse::<f64>(), b.parse::<f64>()) {
158 if let Some(ordering) = a.partial_cmp(&b) {
159 break 'ordering ordering;
160 }
161 }
162
163 natural_cmp(a, b)
164 }
165
166 SortingAttr::Location => {
167 let a: *const &str = a;
168 let b: *const &str = b;
169 a.cmp(&b)
170 }
171 };
172
173 if ordering != Ordering::Equal {
174 return ordering;
175 }
176 }
177
178 Ordering::Equal
179 }
180}
181