use eyre::Result; use regex::{Captures, Regex}; fn compile_pattern(pattern: &str) -> Result { let mut result = "^".to_owned(); for c in pattern.chars() { if c == '%' { if let Some(real_result) = result.strip_suffix(r"\\\\") { // We end with two backslashes, so this is an escaped backslash and then an // unescaped wildcard. result = real_result.to_owned(); result.push_str(r"\\(.*)"); } else if let Some(real_result) = result.strip_suffix(r"\\") { // We end with one backslash, so this is an escaped wildcard. result = real_result.to_owned(); result.push('%'); } else { // We don't end with a backslash, so this is an unescaped wildcard. result.push_str(r"(.*)"); } } else { result.push_str(®ex::escape(&c.to_string())); } } result.push('$'); Ok(Regex::new(&result)?) } pub fn r#match<'a>(pattern: &str, text: &'a str) -> Result>> { Ok(compile_pattern(pattern)?.captures(text)) } #[cfg(test)] mod test { use super::*; type R = Result<()>; #[test] fn pattern_backslashes() -> R { let test_case = compile_pattern(r"the\%weird\\%pattern\\")?; assert_eq!(test_case.to_string(), r"^the%weird\\(.*)pattern\\\\$"); let hell = compile_pattern(r"\\\\%")?; assert_eq!(hell.to_string(), r"^\\\\\\(.*)$"); Ok(()) } }