1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
use std::borrow::Cow;
use crate::Result;
use regex::bytes::Regex;
#[cfg(test)]
mod tests;
mod validate;
pub use validate::{validate_replace, InvalidReplaceCapture};
pub(crate) struct Replacer {
regex: Regex,
replace_with: Vec<u8>,
is_literal: bool,
replacements: usize,
}
impl Replacer {
pub(crate) fn new(
look_for: String,
replace_with: String,
is_literal: bool,
flags: Option<String>,
replacements: usize,
) -> Result<Self> {
let (look_for, replace_with) = if is_literal {
(regex::escape(&look_for), replace_with.into_bytes())
} else {
validate_replace(&replace_with)?;
(
look_for,
unescape::unescape(&replace_with)
.unwrap_or(replace_with)
.into_bytes(),
)
};
let mut regex = regex::bytes::RegexBuilder::new(&look_for);
regex.multi_line(true);
if let Some(flags) = flags {
flags.chars().for_each(|c| {
#[rustfmt::skip]
match c {
'c' => { regex.case_insensitive(false); },
'i' => { regex.case_insensitive(true); },
'm' => {},
'e' => { regex.multi_line(false); },
's' => {
if !flags.contains('m') {
regex.multi_line(false);
}
regex.dot_matches_new_line(true);
},
'w' => {
regex = regex::bytes::RegexBuilder::new(&format!(
"\\b{}\\b",
look_for
));
},
_ => {},
};
});
};
Ok(Self {
regex: regex.build()?,
replace_with,
is_literal,
replacements,
})
}
pub(crate) fn replace<'a>(&'a self, content: &'a [u8]) -> Cow<'a, [u8]> {
let regex = &self.regex;
let limit = self.replacements;
let use_color = false;
if self.is_literal {
Self::replacen(
regex,
limit,
content,
use_color,
regex::bytes::NoExpand(&self.replace_with),
)
} else {
Self::replacen(
regex,
limit,
content,
use_color,
&*self.replace_with,
)
}
}
/// A modified form of [`regex::bytes::Regex::replacen`] that supports
/// coloring replacements
pub(crate) fn replacen<'haystack, R: regex::bytes::Replacer>(
regex: ®ex::bytes::Regex,
limit: usize,
haystack: &'haystack [u8],
_use_color: bool,
mut rep: R,
) -> Cow<'haystack, [u8]> {
let mut it = regex.captures_iter(haystack).enumerate().peekable();
if it.peek().is_none() {
return Cow::Borrowed(haystack);
}
let mut new = Vec::with_capacity(haystack.len());
let mut last_match = 0;
for (i, cap) in it {
// unwrap on 0 is OK because captures only reports matches
let m = cap.get(0).unwrap();
new.extend_from_slice(&haystack[last_match..m.start()]);
rep.replace_append(&cap, &mut new);
last_match = m.end();
if limit > 0 && i >= limit - 1 {
break;
}
}
new.extend_from_slice(&haystack[last_match..]);
Cow::Owned(new)
}
}
|