1 | //! Datastructures and operations used for normalizing test output. |
---|---|
2 | |
3 | use crate::display; |
4 | use bstr::ByteSlice; |
5 | use regex::bytes::{Captures, Regex}; |
6 | use std::borrow::Cow; |
7 | use std::path::Path; |
8 | use std::sync::OnceLock; |
9 | |
10 | /// A filter's match rule. |
11 | #[derive(Clone, Debug)] |
12 | pub enum Match { |
13 | /// If the regex matches, the filter applies |
14 | Regex(Regex), |
15 | /// If the exact byte sequence is found, the filter applies |
16 | Exact(Vec<u8>), |
17 | /// Uses a heuristic to find backslashes in windows style paths |
18 | PathBackslash, |
19 | } |
20 | |
21 | impl Match { |
22 | pub(crate) fn replace_all<'a>(&self, text: &'a [u8], replacement: &[u8]) -> Cow<'a, [u8]> { |
23 | match self { |
24 | Match::Regex(regex) => regex.replace_all(text, replacement), |
25 | Match::Exact(needle) => text.replace(needle, replacement).into(), |
26 | Match::PathBackslash => { |
27 | static PATH_RE: OnceLock<Regex> = OnceLock::new(); |
28 | PATH_RE |
29 | .get_or_init(|| { |
30 | Regex::new( |
31 | r"(?x) |
32 | (?: |
33 | # Match paths to files with extensions that don't include spaces |
34 | \\(?:[\pL\pN.\-_']+[/\\])*[\pL\pN.\-_']+\.\pL+ |
35 | | |
36 | # Allow spaces in absolute paths |
37 | [A-Z]:\\(?:[\pL\pN.\-_'\ ]+[/\\])+ |
38 | )", |
39 | ) |
40 | .unwrap() |
41 | }) |
42 | .replace_all(text, |caps: &Captures<'_>| { |
43 | caps[0].replace(r"\", replacement) |
44 | }) |
45 | } |
46 | } |
47 | } |
48 | } |
49 | |
50 | impl From<&'_ Path> for Match { |
51 | fn from(v: &Path) -> Self { |
52 | let mut v: String = display(path:v); |
53 | // Normalize away windows canonicalized paths. |
54 | if v.starts_with(r"//?/") { |
55 | v.drain(range:0..4); |
56 | } |
57 | Self::Exact(v.into_bytes()) |
58 | } |
59 | } |
60 | |
61 | impl From<Regex> for Match { |
62 | fn from(v: Regex) -> Self { |
63 | Self::Regex(v) |
64 | } |
65 | } |
66 |