1
+ mod line_replacer;
2
+
1
3
use crate :: { utils, Error , Result } ;
2
4
use regex:: bytes:: Regex ;
3
- use std:: { fs, fs:: File , io:: prelude:: * , path:: Path } ;
5
+ use std:: { borrow :: Cow , fs, fs:: File , io:: prelude:: * , path:: Path } ;
4
6
5
- pub ( crate ) struct Replacer {
7
+ #[ derive( Debug ) ]
8
+ struct Pair {
6
9
regex : Regex ,
7
- replace_with : Vec < u8 > ,
8
- is_literal : bool ,
9
- replacements : usize ,
10
+ rep : Vec < u8 > ,
11
+ }
12
+
13
+ #[ derive( Debug ) ]
14
+ pub ( crate ) struct Replacer {
15
+ pairs : Vec < Pair > ,
16
+ is_literal : bool , // -s
17
+ max_replacements : usize ,
10
18
}
11
19
20
+
12
21
impl Replacer {
13
22
pub ( crate ) fn new (
14
23
look_for : String ,
15
24
replace_with : String ,
16
25
is_literal : bool ,
17
26
flags : Option < String > ,
18
27
replacements : Option < usize > ,
28
+ extra : Vec < String > ,
19
29
) -> Result < Self > {
20
- let ( look_for, replace_with) = if is_literal {
21
- ( regex:: escape ( & look_for) , replace_with. into_bytes ( ) )
22
- } else {
23
- (
24
- look_for,
25
- utils:: unescape ( & replace_with)
26
- . unwrap_or ( replace_with)
27
- . into_bytes ( ) ,
28
- )
29
- } ;
30
-
31
- let mut regex = regex:: bytes:: RegexBuilder :: new ( & look_for) ;
32
- regex. multi_line ( true ) ;
33
-
34
- if let Some ( flags) = flags {
35
- flags. chars ( ) . for_each ( |c| {
36
- #[ rustfmt:: skip]
30
+ fn create (
31
+ look_for : String ,
32
+ replace_with : String ,
33
+ is_literal : bool ,
34
+ flags : Option < & str > ,
35
+ ) -> Result < Pair > {
36
+ let ( look_for, replace_with) = if is_literal {
37
+ ( regex:: escape ( & look_for) , replace_with. into_bytes ( ) )
38
+ } else {
39
+ (
40
+ look_for,
41
+ utils:: unescape ( & replace_with)
42
+ . unwrap_or ( replace_with)
43
+ . into_bytes ( ) ,
44
+ )
45
+ } ;
46
+
47
+ let mut regex = regex:: bytes:: RegexBuilder :: new ( & look_for) ;
48
+ regex. multi_line ( true ) ;
49
+
50
+ if let Some ( flags) = flags {
51
+ for c in flags. chars ( ) {
52
+ #[ rustfmt:: skip]
37
53
match c {
38
54
'c' => { regex. case_insensitive ( false ) ; } ,
39
55
'i' => { regex. case_insensitive ( true ) ; } ,
@@ -53,19 +69,46 @@ impl Replacer {
53
69
} ,
54
70
_ => { } ,
55
71
} ;
56
- } ) ;
57
- } ;
72
+ }
73
+ } ;
74
+ Ok ( Pair {
75
+ regex : regex. build ( ) ?,
76
+ rep : replace_with,
77
+ } )
78
+ }
58
79
59
- Ok ( Self {
60
- regex : regex. build ( ) ?,
80
+ let capacity = extra. len ( ) / 2 + 1 ;
81
+ let mut pairs = Vec :: with_capacity ( capacity) ;
82
+ pairs. push ( create (
83
+ look_for,
61
84
replace_with,
62
85
is_literal,
63
- replacements : replacements. unwrap_or ( 0 ) ,
86
+ flags. as_deref ( ) ,
87
+ ) ?) ;
88
+
89
+ let mut it = extra. into_iter ( ) ;
90
+ while let Some ( look_for) = it. next ( ) {
91
+ let replace_with = it
92
+ . next ( )
93
+ . expect ( "The extra pattern list doesn't have an even lenght" ) ;
94
+
95
+ pairs. push ( create (
96
+ look_for,
97
+ replace_with,
98
+ is_literal,
99
+ flags. as_deref ( ) ,
100
+ ) ?) ;
101
+ }
102
+
103
+ Ok ( Self {
104
+ pairs,
105
+ is_literal,
106
+ max_replacements : replacements. unwrap_or ( 0 ) ,
64
107
} )
65
108
}
66
109
67
110
pub ( crate ) fn has_matches ( & self , content : & [ u8 ] ) -> bool {
68
- self . regex . is_match ( content)
111
+ self . pairs . iter ( ) . any ( |r| r . regex . is_match ( content) )
69
112
}
70
113
71
114
pub ( crate ) fn check_not_empty ( mut file : File ) -> Result < ( ) > {
@@ -74,50 +117,56 @@ impl Replacer {
74
117
Ok ( ( ) )
75
118
}
76
119
77
- pub ( crate ) fn replace < ' a > (
78
- & ' a self ,
79
- content : & ' a [ u8 ] ,
80
- ) -> std:: borrow:: Cow < ' a , [ u8 ] > {
81
- if self . is_literal {
82
- self . regex . replacen (
83
- content,
84
- self . replacements ,
85
- regex:: bytes:: NoExpand ( & self . replace_with ) ,
86
- )
87
- } else {
88
- self . regex
89
- . replacen ( content, self . replacements , & * self . replace_with )
120
+ pub ( crate ) fn replace < ' a > ( & ' a self , content : & ' a [ u8 ] ) -> Cow < ' a , [ u8 ] > {
121
+ let mut result = Cow :: Borrowed ( content) ;
122
+ for Pair { regex, rep } in self . pairs . iter ( ) {
123
+ let res = if self . is_literal {
124
+ let rep = regex:: bytes:: NoExpand ( rep. as_slice ( ) ) ;
125
+ regex. replacen ( & result, self . max_replacements , rep)
126
+ } else {
127
+ regex. replacen ( & result, self . max_replacements , rep)
128
+ } ;
129
+
130
+ result = Cow :: Owned ( res. into_owned ( ) ) ;
90
131
}
132
+ result
91
133
}
92
134
93
135
pub ( crate ) fn replace_preview < ' a > (
94
136
& ' a self ,
95
- content : & [ u8 ] ,
96
- ) -> std:: borrow:: Cow < ' a , [ u8 ] > {
97
- let mut v = Vec :: < u8 > :: new ( ) ;
98
- let mut captures = self . regex . captures_iter ( content) ;
99
-
100
- self . regex . split ( content) . for_each ( |sur_text| {
101
- use regex:: bytes:: Replacer ;
102
-
103
- v. extend ( sur_text) ;
104
- if let Some ( capture) = captures. next ( ) {
105
- v. extend_from_slice (
106
- ansi_term:: Color :: Green . prefix ( ) . to_string ( ) . as_bytes ( ) ,
107
- ) ;
108
- if self . is_literal {
109
- regex:: bytes:: NoExpand ( & self . replace_with )
110
- . replace_append ( & capture, & mut v) ;
111
- } else {
112
- ( & * self . replace_with ) . replace_append ( & capture, & mut v) ;
137
+ content : & ' a [ u8 ] ,
138
+ ) -> Cow < ' a , [ u8 ] > {
139
+ let mut content = Cow :: Borrowed ( content) ;
140
+
141
+ for Pair { regex, rep } in self . pairs . iter ( ) {
142
+ let rep = rep. as_slice ( ) ;
143
+
144
+ let mut v = Vec :: < u8 > :: new ( ) ;
145
+ let mut captures = regex. captures_iter ( & content) ;
146
+
147
+ for sur_text in regex. split ( & content) {
148
+ use regex:: bytes:: Replacer ;
149
+
150
+ v. extend ( sur_text) ;
151
+ if let Some ( capture) = captures. next ( ) {
152
+ v. extend_from_slice (
153
+ ansi_term:: Color :: Green . prefix ( ) . to_string ( ) . as_bytes ( ) ,
154
+ ) ;
155
+ if self . is_literal {
156
+ regex:: bytes:: NoExpand ( & rep)
157
+ . replace_append ( & capture, & mut v) ;
158
+ } else {
159
+ ( & * rep) . replace_append ( & capture, & mut v) ;
160
+ }
161
+ v. extend_from_slice (
162
+ ansi_term:: Color :: Green . suffix ( ) . to_string ( ) . as_bytes ( ) ,
163
+ ) ;
113
164
}
114
- v. extend_from_slice (
115
- ansi_term:: Color :: Green . suffix ( ) . to_string ( ) . as_bytes ( ) ,
116
- ) ;
117
165
}
118
- } ) ;
166
+ content = Cow :: Owned ( v) ;
167
+ }
119
168
120
- return std :: borrow :: Cow :: Owned ( v ) ;
169
+ content
121
170
}
122
171
123
172
pub ( crate ) fn replace_file ( & self , path : & Path ) -> Result < ( ) > {
@@ -130,7 +179,7 @@ impl Replacer {
130
179
131
180
let source = File :: open ( path) ?;
132
181
let meta = fs:: metadata ( path) ?;
133
- let mmap_source = unsafe { Mmap :: map ( & source) ? } ;
182
+ let mmap_source = unsafe { Mmap :: map ( & source) } ? ;
134
183
let replaced = self . replace ( & mmap_source) ;
135
184
136
185
let target = tempfile:: NamedTempFile :: new_in (
@@ -142,7 +191,7 @@ impl Replacer {
142
191
file. set_permissions ( meta. permissions ( ) ) ?;
143
192
144
193
if !replaced. is_empty ( ) {
145
- let mut mmap_target = unsafe { MmapMut :: map_mut ( file) ? } ;
194
+ let mut mmap_target = unsafe { MmapMut :: map_mut ( file) } ? ;
146
195
mmap_target. deref_mut ( ) . write_all ( & replaced) ?;
147
196
mmap_target. flush_async ( ) ?;
148
197
}
@@ -173,6 +222,7 @@ mod tests {
173
222
literal,
174
223
flags. map ( ToOwned :: to_owned) ,
175
224
None ,
225
+ vec ! [ ] ,
176
226
)
177
227
. unwrap ( ) ;
178
228
assert_eq ! (
@@ -216,4 +266,22 @@ mod tests {
216
266
fn full_word_replace ( ) {
217
267
replace ( "abc" , "def" , false , Some ( "w" ) , "abcd abc" , "abcd def" ) ;
218
268
}
269
+
270
+ #[ test]
271
+ fn test_multipattern ( ) {
272
+ let replacer = Replacer :: new (
273
+ "foo" . to_owned ( ) ,
274
+ "bar" . to_owned ( ) ,
275
+ false ,
276
+ None ,
277
+ None ,
278
+ vec ! [ "qux" . into( ) , "quux" . into( ) , "bing" . into( ) , "bong" . into( ) ] ,
279
+ )
280
+ . unwrap ( ) ;
281
+
282
+ assert_eq ! (
283
+ std:: str :: from_utf8( & replacer. replace( "foo qux bing" . as_bytes( ) ) ) ,
284
+ Ok ( "bar quux bong" )
285
+ ) ;
286
+ }
219
287
}
0 commit comments