logcheck_fluent_bit_filter/
regex_conversion.rs1use once_cell::sync::Lazy;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct RegexConversion {
11 pub modern: &'static str,
13 pub posix: &'static str,
15 pub description: &'static str,
17}
18
19pub static REGEX_CONVERSIONS: Lazy<Vec<RegexConversion>> = Lazy::new(|| {
21 vec![
22 RegexConversion {
23 modern: r"\d",
24 posix: "[[:digit:]]",
25 description: "Digits 0-9",
26 },
27 RegexConversion {
28 modern: r"\D",
29 posix: "[^[:digit:]]",
30 description: "Non-digits",
31 },
32 RegexConversion {
33 modern: r"\s",
34 posix: "[[:space:]]",
35 description: "Whitespace characters",
36 },
37 RegexConversion {
38 modern: r"\S",
39 posix: "[^[:space:]]",
40 description: "Non-whitespace characters",
41 },
42 RegexConversion {
44 modern: r"\w",
45 posix: "[[:alnum:]_]",
46 description: "Word characters (letters, digits, underscore)",
47 },
48 RegexConversion {
49 modern: r"\W",
50 posix: "[^[:alnum:]_]",
51 description: "Non-word characters",
52 },
53 ]
54});
55
56pub static POSIX_ONLY_CONVERSIONS: Lazy<Vec<(&'static str, &'static str)>> = Lazy::new(|| {
58 vec![
59 ("[[:alpha:]]", "a-zA-Z"),
60 ("[[:alnum:]]", "a-zA-Z0-9"),
61 ("[[:xdigit:]]", "0-9a-fA-F"),
62 ("[[:lower:]]", "a-z"),
63 ("[[:upper:]]", "A-Z"),
64 ("[[:blank:]]", " \\t"),
65 ("[[:punct:]]", "!\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~"),
66 ("[[:print:]]", "\\x20-\\x7E"),
67 ("[[:graph:]]", "!-~"),
68 ("[[:cntrl:]]", "\\x00-\\x1F\\x7F"),
69 ]
70});
71
72pub fn modern_to_posix(pattern: &str) -> String {
84 let mut result = pattern.to_string();
85
86 for conversion in REGEX_CONVERSIONS.iter() {
88 result = result.replace(conversion.modern, conversion.posix);
89 }
90
91 result
92}
93
94pub fn posix_to_modern(pattern: &str) -> String {
106 let mut result = pattern.to_string();
107
108 for conversion in REGEX_CONVERSIONS.iter() {
110 result = result.replace(conversion.posix, conversion.modern);
111 }
112
113 for (posix_class, rust_equiv) in POSIX_ONLY_CONVERSIONS.iter() {
115 result = result.replace(posix_class, rust_equiv);
116 }
117
118 result
119}
120
121pub fn posix_to_rust(pattern: &str) -> String {
133 let mut result = pattern.to_string();
134
135 for conversion in REGEX_CONVERSIONS.iter() {
137 result = result.replace(conversion.posix, conversion.modern);
138 }
139
140 for (posix_class, rust_equiv) in POSIX_ONLY_CONVERSIONS.iter() {
142 result = result.replace(posix_class, rust_equiv);
143 }
144
145 result = result.replace(" { ", " \\{ ");
147 result = result.replace(" } ", " \\} ");
148
149 result
150}
151
152pub fn remove_anchors(pattern: &str) -> String {
156 let mut result = pattern.to_string();
157
158 if result.starts_with('^') {
160 result = result[1..].to_string();
161 }
162
163 if result.ends_with('$') {
165 result = result[..result.len() - 1].to_string();
166 }
167
168 result
169}
170
171pub fn ensure_anchors(pattern: &str) -> String {
173 let mut result = pattern.to_string();
174
175 if !result.starts_with('^') {
176 result = format!("^{}", result);
177 }
178
179 if !result.ends_with('$') {
180 result = format!("{}$", result);
181 }
182
183 result
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_modern_to_posix() {
192 assert_eq!(modern_to_posix(r"\d"), "[[:digit:]]");
193 assert_eq!(
194 modern_to_posix(r"\d\d\d"),
195 "[[:digit:]][[:digit:]][[:digit:]]"
196 );
197 assert_eq!(modern_to_posix(r"\w+"), "[[:alnum:]_]+");
198 assert_eq!(modern_to_posix(r"\s"), "[[:space:]]");
199 assert_eq!(modern_to_posix(r"\D"), "[^[:digit:]]");
200 assert_eq!(modern_to_posix(r"\W"), "[^[:alnum:]_]");
201 assert_eq!(modern_to_posix(r"\S"), "[^[:space:]]");
202 }
203
204 #[test]
205 fn test_posix_to_modern() {
206 assert_eq!(posix_to_modern("[[:digit:]]"), r"\d");
207 assert_eq!(
208 posix_to_modern("[[:digit:]][[:digit:]][[:digit:]]"),
209 r"\d\d\d"
210 );
211 assert_eq!(posix_to_modern("[[:alnum:]_]+"), r"\w+");
212 assert_eq!(posix_to_modern("[[:space:]]"), r"\s");
213 assert_eq!(posix_to_modern("[^[:digit:]]"), r"\D");
214 }
215
216 #[test]
217 fn test_posix_to_rust() {
218 assert_eq!(posix_to_rust("[[:digit:]]"), r"\d");
219 assert_eq!(posix_to_rust("[[:alpha:]]"), "a-zA-Z");
220 assert_eq!(posix_to_rust("[[:alnum:]]"), "a-zA-Z0-9");
221 assert_eq!(posix_to_rust("[[:space:]]"), r"\s");
222 }
223
224 #[test]
225 fn test_roundtrip() {
226 let modern = r"\d+\s\w+";
227 let posix = modern_to_posix(modern);
228 assert_eq!(posix, "[[:digit:]]+[[:space:]][[:alnum:]_]+");
229 let back_to_modern = posix_to_modern(&posix);
230 assert_eq!(back_to_modern, modern);
231 }
232
233 #[test]
234 fn test_remove_anchors() {
235 assert_eq!(remove_anchors("^test$"), "test");
236 assert_eq!(remove_anchors("^test"), "test");
237 assert_eq!(remove_anchors("test$"), "test");
238 assert_eq!(remove_anchors("test"), "test");
239 }
240
241 #[test]
242 fn test_ensure_anchors() {
243 assert_eq!(ensure_anchors("test"), "^test$");
244 assert_eq!(ensure_anchors("^test"), "^test$");
245 assert_eq!(ensure_anchors("test$"), "^test$");
246 assert_eq!(ensure_anchors("^test$"), "^test$");
247 }
248
249 #[test]
250 fn test_complex_pattern() {
251 let grex_output = r"^pam_unix\(sudo:session\): session \w+ for user \w+$";
252 let posix = modern_to_posix(grex_output);
253 assert_eq!(
254 posix,
255 "^pam_unix\\(sudo:session\\): session [[:alnum:]_]+ for user [[:alnum:]_]+$"
256 );
257 }
258}