Skip to content

Commit 2fe93ef

Browse files
committed
Expose the substitute method of pcre as Regex::substitute and
Regex::substitute_all.
1 parent df0aff5 commit 2fe93ef

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

src/bytes.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,58 @@ impl Regex {
588588
) -> CaptureMatches<'r, 's> {
589589
CaptureMatches { re: self, subject, last_end: 0, last_match: None }
590590
}
591+
592+
/// Replaces the first match in `subject` with the `replacement`,
593+
/// and puts the replaced string in `output`.
594+
/// ```rust
595+
/// # fn example() -> Result<(), ::pcre2::Error> {
596+
/// use std::str;
597+
///
598+
/// use pcre2::bytes::Regex;
599+
///
600+
/// let re = Regex::new(r"mike")?;
601+
/// let text = b"Hi mike, wait you are not mike.";
602+
/// let mut output = Vec::new();
603+
/// re.substitute(text, b"john", &mut output).unwrap();
604+
/// assert_eq!(&output, b"Hi john, wait you are not mike.");
605+
/// # Ok(()) }; example().unwrap()
606+
/// ```
607+
pub fn substitute(
608+
&self,
609+
subject: &[u8],
610+
replacement: &[u8],
611+
output: &mut Vec<u8>,
612+
) -> Result<usize, Error> {
613+
self.code.substitute(subject, replacement, output, 0)
614+
}
615+
/// Replaces all the matches in `subject` with the `replacement`,
616+
/// and puts the replaced string in `output`.
617+
/// ```rust
618+
/// # fn example() -> Result<(), ::pcre2::Error> {
619+
/// use std::str;
620+
///
621+
/// use pcre2::bytes::Regex;
622+
///
623+
/// let re = Regex::new(r"mike")?;
624+
/// let text = b"Hi mike, wait you are not mike.";
625+
/// let mut output = Vec::new();
626+
/// re.substitute_all(text, b"john", &mut output).unwrap();
627+
/// assert_eq!(&output, b"Hi john, wait you are not john.");
628+
/// # Ok(()) }; example().unwrap()
629+
/// ```
630+
pub fn substitute_all(
631+
&self,
632+
subject: &[u8],
633+
replacement: &[u8],
634+
output: &mut Vec<u8>,
635+
) -> Result<usize, Error> {
636+
self.code.substitute(
637+
subject,
638+
replacement,
639+
output,
640+
pcre2_sys::PCRE2_SUBSTITUTE_GLOBAL,
641+
)
642+
}
591643
}
592644

593645
/// Advanced or "lower level" search methods.
@@ -1370,4 +1422,28 @@ mod tests {
13701422
let matched = re.find(hay.as_bytes()).unwrap().unwrap();
13711423
assert_eq!(matched.as_bytes(), "😀👍🏼🎉".as_bytes());
13721424
}
1425+
#[test]
1426+
fn test_substitute() {
1427+
let hay = "0123456789abcdefghijklmnopqrstuvwxyzABCDKLMNOPQRSTUVWXYZ";
1428+
let pattern = r"(?i)abcd";
1429+
let re = Regex::new(pattern).unwrap();
1430+
let mut output = Vec::new();
1431+
re.substitute(hay.as_bytes(), b"42", &mut output).unwrap();
1432+
assert_eq!(
1433+
&output,
1434+
b"012345678942efghijklmnopqrstuvwxyzABCDKLMNOPQRSTUVWXYZ"
1435+
);
1436+
}
1437+
#[test]
1438+
fn test_substitute_all() {
1439+
let hay = "0123456789abcdefghijklmnopqrstuvwxyzABCDKLMNOPQRSTUVWXYZ";
1440+
let pattern = r"(?i)abcd";
1441+
let re = Regex::new(pattern).unwrap();
1442+
let mut output = Vec::new();
1443+
re.substitute_all(hay.as_bytes(), b"42", &mut output).unwrap();
1444+
assert_eq!(
1445+
&output,
1446+
b"012345678942efghijklmnopqrstuvwxyz42KLMNOPQRSTUVWXYZ"
1447+
);
1448+
}
13731449
}

src/ffi.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,60 @@ impl Code {
258258
Ok(1 + count as usize)
259259
}
260260
}
261+
262+
/// Substitute the replacement pattern in subject and put the output in
263+
/// output vec. Output vec is will be cleared before use.
264+
pub(crate) fn substitute(
265+
&self,
266+
subject: &[u8],
267+
replacement: &[u8],
268+
output: &mut Vec<u8>,
269+
options: u32,
270+
) -> Result<usize, Error> {
271+
output.clear();
272+
let mut output_length = output.capacity();
273+
let mut rc = unsafe {
274+
pcre2_substitute_8(
275+
self.as_ptr(),
276+
subject.as_ptr(),
277+
subject.len(),
278+
0, //startoffset
279+
PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | options,
280+
ptr::null_mut(), //match_data
281+
ptr::null_mut(), //match context
282+
replacement.as_ptr(),
283+
replacement.len(),
284+
output.as_mut_ptr(),
285+
&mut output_length,
286+
)
287+
};
288+
if rc == PCRE2_ERROR_NOMEMORY {
289+
output.reserve_exact(output_length + 1);
290+
rc = unsafe {
291+
pcre2_substitute_8(
292+
self.as_ptr(),
293+
subject.as_ptr(),
294+
subject.len(),
295+
0, //startoffset
296+
options,
297+
ptr::null_mut(), //match_data
298+
ptr::null_mut(), //match context
299+
replacement.as_ptr(),
300+
replacement.len(),
301+
output.as_mut_ptr(),
302+
&mut output_length,
303+
)
304+
}
305+
}
306+
if rc < 0 {
307+
Err(Error::info(rc))
308+
} else {
309+
// Safety: pcre2_substitute_8 method above would set this
310+
// field correctly.
311+
unsafe { output.set_len(output_length) };
312+
Ok(rc as usize)
313+
}
314+
}
261315
}
262316

263317
/// A low level representation of PCRE2's compilation context.

0 commit comments

Comments
 (0)