summaryrefslogtreecommitdiffstats
path: root/src/utils/regex_replacement.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/regex_replacement.rs')
-rw-r--r--src/utils/regex_replacement.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/utils/regex_replacement.rs b/src/utils/regex_replacement.rs
new file mode 100644
index 00000000..5f07902e
--- /dev/null
+++ b/src/utils/regex_replacement.rs
@@ -0,0 +1,112 @@
+use std::borrow::Cow;
+
+use regex::{Regex, RegexBuilder};
+
+#[derive(Clone, Debug)]
+pub struct RegexReplacement {
+ regex: Regex,
+ replacement: String,
+ replace_all: bool,
+}
+
+impl RegexReplacement {
+ pub fn from_sed_command(sed_command: &str) -> Option<Self> {
+ let sep = sed_command.chars().nth(1)?;
+ let mut parts = sed_command[2..].split(sep);
+ let regex = parts.next()?;
+ let replacement = parts.next()?.to_string();
+ let flags = parts.next()?;
+ let mut re_builder = RegexBuilder::new(regex);
+ let mut replace_all = false;
+ for flag in flags.chars() {
+ match flag {
+ 'g' => {
+ replace_all = true;
+ }
+ 'i' => {
+ re_builder.case_insensitive(true);
+ }
+ 'm' => {
+ re_builder.multi_line(true);
+ }
+ 's' => {
+ re_builder.dot_matches_new_line(true);
+ }
+ 'U' => {
+ re_builder.swap_greed(true);
+ }
+ 'x' => {
+ re_builder.ignore_whitespace(true);
+ }
+ _ => {}
+ }
+ }
+ let regex = re_builder.build().ok()?;
+ Some(RegexReplacement {
+ regex,
+ replacement,
+ replace_all,
+ })
+ }
+
+ pub fn execute<'t>(&self, s: &'t str) -> Cow<'t, str> {
+ if self.replace_all {
+ self.regex.replace_all(s, &self.replacement)
+ } else {
+ self.regex.replace(s, &self.replacement)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_sed_command() {
+ let command = "s,foo,bar,";
+ let rr = RegexReplacement::from_sed_command(command).unwrap();
+ assert_eq!(rr.regex.as_str(), "foo");
+ assert_eq!(rr.replacement, "bar");
+ assert_eq!(rr.replace_all, false);
+ assert_eq!(rr.execute("foo"), "bar");
+ }
+
+ #[test]
+ fn test_sed_command_i_flag() {
+ let command = "s,FOO,bar,";
+ let rr = RegexReplacement::from_sed_command(command).unwrap();
+ assert_eq!(rr.execute("foo"), "foo");
+ let command = "s,FOO,bar,i";
+ let rr = RegexReplacement::from_sed_command(command).unwrap();
+ assert_eq!(rr.execute("foo"), "bar");
+ }
+
+ #[test]
+ fn test_sed_command_g_flag() {
+ let command = "s,foo,bar,";
+ let rr = RegexReplacement::from_sed_command(command).unwrap();
+ assert_eq!(rr.execute("foofoo"), "barfoo");
+ let command = "s,foo,bar,g";
+ let rr = RegexReplacement::from_sed_command(command).unwrap();
+ assert_eq!(rr.execute("foofoo"), "barbar");
+ }
+
+ #[test]
+ fn test_sed_command_with_named_captures() {
+ let command = r"s/(?P<last>[^,\s]+),\s+(?P<first>\S+)/$first $last/";
+ let rr = RegexReplacement::from_sed_command(command).unwrap();
+ assert_eq!(rr.execute("Springsteen, Bruce"), "Bruce Springsteen");
+ }
+
+ #[test]
+ fn test_sed_command_invalid() {
+ assert!(RegexReplacement::from_sed_command("").is_none());
+ assert!(RegexReplacement::from_sed_command("s").is_none());
+ assert!(RegexReplacement::from_sed_command("s,").is_none());
+ assert!(RegexReplacement::from_sed_command("s,,").is_none());
+ assert!(RegexReplacement::from_sed_command("s,,i").is_none());
+ assert!(RegexReplacement::from_sed_command("s,,,").is_some());
+ assert!(RegexReplacement::from_sed_command("s,,,i").is_some());
+ }
+}