1use anyhow::{Context, Result};
2use regex::Regex;
3
4pub fn compile_filters(patterns: &[String]) -> Result<Vec<Regex>> {
6 patterns
7 .iter()
8 .map(|pattern| {
9 Regex::new(pattern).with_context(|| format!("Invalid regex pattern: '{}'", pattern))
10 })
11 .collect()
12}
13
14#[must_use]
17pub fn measurements_to_anchored_regex(measurements: &[String]) -> Vec<String> {
18 measurements
19 .iter()
20 .map(|m| format!("^{}$", regex::escape(m)))
21 .collect()
22}
23
24#[must_use]
27pub fn combine_measurements_and_filters(
28 measurements: &[String],
29 filter_patterns: &[String],
30) -> Vec<String> {
31 let mut combined = measurements_to_anchored_regex(measurements);
32 combined.extend_from_slice(filter_patterns);
33 combined
34}
35
36#[must_use]
39pub fn matches_any_filter(name: &str, filters: &[Regex]) -> bool {
40 if filters.is_empty() {
41 return true; }
43 filters.iter().any(|re| re.is_match(name))
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn test_compile_valid_filters() {
52 let patterns = vec!["bench.*".to_string(), "test_.*".to_string()];
53 let result = compile_filters(&patterns);
54 assert!(result.is_ok());
55 assert_eq!(result.unwrap().len(), 2);
56 }
57
58 #[test]
59 fn test_compile_invalid_regex() {
60 let patterns = vec!["[invalid".to_string()];
61 let result = compile_filters(&patterns);
62 assert!(result.is_err());
63 }
64
65 #[test]
66 fn test_matches_any_filter_empty() {
67 let filters = vec![];
68 assert!(matches_any_filter("anything", &filters));
69 }
70
71 #[test]
72 fn test_matches_any_filter_single_match() {
73 let patterns = vec!["bench.*".to_string()];
74 let filters = compile_filters(&patterns).unwrap();
75 assert!(matches_any_filter("benchmark_x64", &filters));
76 assert!(!matches_any_filter("test_foo", &filters));
77 }
78
79 #[test]
80 fn test_matches_any_filter_or_logic() {
81 let patterns = vec!["bench.*".to_string(), "test_.*".to_string()];
82 let filters = compile_filters(&patterns).unwrap();
83 assert!(matches_any_filter("benchmark_x64", &filters));
84 assert!(matches_any_filter("test_foo", &filters));
85 assert!(!matches_any_filter("other_thing", &filters));
86 }
87
88 #[test]
89 fn test_anchored_patterns() {
90 let patterns = vec!["^bench.*$".to_string()];
91 let filters = compile_filters(&patterns).unwrap();
92 assert!(matches_any_filter("benchmark_x64", &filters));
93 assert!(!matches_any_filter("my_benchmark_x64", &filters));
94 }
95
96 #[test]
97 fn test_complex_regex() {
98 let patterns = vec![r"bench_.*_v\d+".to_string()];
99 let filters = compile_filters(&patterns).unwrap();
100 assert!(matches_any_filter("bench_foo_v1", &filters));
101 assert!(matches_any_filter("bench_bar_v23", &filters));
102 assert!(!matches_any_filter("bench_baz_vX", &filters));
103 }
104
105 #[test]
106 fn test_measurements_to_anchored_regex() {
107 let measurements = vec![
108 "benchmark_x64".to_string(),
109 "test.with.dots".to_string(),
110 "name[with]brackets".to_string(),
111 ];
112 let anchored = measurements_to_anchored_regex(&measurements);
113
114 assert_eq!(anchored.len(), 3);
115 assert_eq!(anchored[0], "^benchmark_x64$");
116 assert_eq!(anchored[1], r"^test\.with\.dots$");
117 assert_eq!(anchored[2], r"^name\[with\]brackets$");
118 }
119
120 #[test]
121 fn test_measurements_to_anchored_regex_matches_exactly() {
122 let measurements = vec!["benchmark".to_string()];
123 let anchored = measurements_to_anchored_regex(&measurements);
124 let filters = compile_filters(&anchored).unwrap();
125
126 assert!(matches_any_filter("benchmark", &filters));
128
129 assert!(!matches_any_filter("benchmark_x64", &filters));
131 assert!(!matches_any_filter("my_benchmark", &filters));
132 }
133
134 #[test]
135 fn test_combine_measurements_and_filters() {
136 let measurements = vec!["exact_match".to_string()];
137 let filters = vec!["pattern.*".to_string()];
138
139 let combined = combine_measurements_and_filters(&measurements, &filters);
140
141 assert_eq!(combined.len(), 2);
142 assert_eq!(combined[0], "^exact_match$");
143 assert_eq!(combined[1], "pattern.*");
144 }
145
146 #[test]
147 fn test_combine_measurements_and_filters_matching() {
148 let measurements = vec!["benchmark_x64".to_string()];
149 let filters = vec!["test_.*".to_string()];
150
151 let combined = combine_measurements_and_filters(&measurements, &filters);
152 let compiled = compile_filters(&combined).unwrap();
153
154 assert!(matches_any_filter("benchmark_x64", &compiled));
156
157 assert!(matches_any_filter("test_foo", &compiled));
159 assert!(matches_any_filter("test_bar", &compiled));
160
161 assert!(!matches_any_filter("benchmark_arm64", &compiled));
163 assert!(!matches_any_filter("other", &compiled));
164 }
165}