use regex::{Captures, Regex}; fn compile_pattern(pattern: &str) -> Regex { let mut result = String::new(); for c in pattern.chars() { match c { // This is a nightmare, because we're escaping for regex syntax before we put // things into result. // We don't end with a backslash, so this is an unescaped wildcard. '%' if !result.ends_with(r"\\") => { result.push_str(r"(\w*)"); } // We end with two backslashes, so this is an escaped backslash and then an // unescaped wildcard. '%' if result.ends_with(r"\\\\") => { result = result.strip_suffix(r"\\\\").unwrap().to_string(); result.push_str(r"\\(\w*)"); } // We end with one backslash, so this is an escaped wildcard. '%' if result.ends_with(r"\\") => { result = result.strip_suffix(r"\\").unwrap().to_string(); result.push('%'); } _ => result.push_str(®ex::escape(&c.to_string())), } } Regex::new(&result).expect("built invalid regex!") } pub(crate) fn r#match<'a>(pattern: &str, text: &'a str) -> Option> { compile_pattern(pattern).captures(text) } #[cfg(test)] mod test { use super::*; #[test] fn pattern_backslashes() { let test_case = compile_pattern(r"the\%weird\\%pattern\\"); assert_eq!(test_case.to_string(), r"the%weird\\(\w*)pattern\\\\"); let hell = compile_pattern(r"\\\\%"); assert_eq!(hell.to_string(), r"\\\\\\(\w*)"); } }