| 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 |  | 
|---|