pub fn test_parse_fails(pattern: &str) {
let res = regress::Regex::new(pattern);
assert!(res.is_err(), "Pattern should not have parsed: {}", pattern);
}
pub fn test_parse_fails_flags(pattern: &str, flags: &str) {
let res = regress::Regex::with_flags(pattern, flags);
assert!(res.is_err(), "Pattern should not have parsed: {}", pattern);
}
fn format_match(r: ®ress::Match, input: &str) -> String {
let mut result = input[r.range()].to_string();
for cg in r.captures.iter() {
result.push(',');
if let Some(cg) = cg {
result.push_str(&input[cg.clone()])
}
}
result
}
pub trait StringTestHelpers {
fn test_eq(&self, s: &str);
}
impl StringTestHelpers for String {
fn test_eq(&self, rhs: &str) {
assert_eq!(self.as_str(), rhs)
}
}
pub trait VecTestHelpers {
fn test_eq(&self, rhs: Vec<&str>);
}
impl VecTestHelpers for Vec<&str> {
fn test_eq(&self, rhs: Vec<&str>) {
assert_eq!(*self, rhs)
}
}
#[derive(Debug, Clone)]
pub struct TestCompiledRegex {
re: regress::Regex,
tc: TestConfig,
}
impl TestCompiledRegex {
pub fn matches(&'_ self, input: &'_ str, start: usize) -> Vec<regress::Match> {
use regress::backends as rbe;
match (self.tc.use_ascii(input), self.tc.backend) {
(true, Backend::PikeVM) => {
rbe::find::<rbe::PikeVMExecutor>(&self.re, input, start).collect()
}
(false, Backend::PikeVM) => {
rbe::find::<rbe::PikeVMExecutor>(&self.re, input, start).collect()
}
(true, Backend::Backtracking) => {
rbe::find_ascii::<rbe::BacktrackExecutor>(&self.re, input, start).collect()
}
(false, Backend::Backtracking) => {
rbe::find::<rbe::BacktrackExecutor>(&self.re, input, start).collect()
}
}
}
pub fn find(&self, input: &str) -> Option<regress::Match> {
self.matches(input, 0).into_iter().next()
}
pub fn match1f(&self, input: &str) -> String {
match self.find(input) {
Some(m) => format_match(&m, input),
None => panic!("Failed to match {}", input),
}
}
pub fn match1_named_group(&self, input: &str, group: &str) -> String {
match self.find(input) {
Some(m) => match m.named_group(group) {
Some(r) => match input.get(r.clone()) {
Some(str) => str.to_string(),
None => panic!("Cannot get range from string input {:?}", r),
},
None => panic!("Named capture group does not exist {}", group),
},
None => panic!("Failed to match {}", input),
}
}
pub fn match1_vec<'a, 'b>(&'a self, input: &'b str) -> Vec<Option<&'b str>> {
let mut result = Vec::new();
let m: regress::Match = self.find(input).expect("Failed to match");
result.push(Some(&input[m.range()]));
for cr in m.captures {
result.push(cr.map(|r| &input[r]));
}
result
}
pub fn test_fails(&self, input: &str) {
assert!(self.find(input).is_none(), "Should not have matched")
}
pub fn test_succeeds(&self, input: &str) {
assert!(self.find(input).is_some(), "Should have matched")
}
pub fn match_all_from(&'_ self, input: &'_ str, start: usize) -> Vec<regress::Range> {
self.matches(input, start)
.into_iter()
.map(move |m| m.range())
.collect()
}
pub fn match_all<'a, 'b>(&'a self, input: &'b str) -> Vec<&'b str> {
self.matches(input, 0)
.into_iter()
.map(move |m| &input[m.range()])
.collect()
}
pub fn run_global_match(&self, input: &str) -> String {
self.matches(input, 0)
.into_iter()
.map(move |m| format_match(&m, input))
.collect::<Vec<String>>()
.join(",")
}
}
#[derive(Debug, Copy, Clone)]
enum Backend {
PikeVM,
Backtracking,
}
#[derive(Debug, Copy, Clone)]
pub struct TestConfig {
ascii: bool,
optimize: bool,
backend: Backend,
}
impl TestConfig {
pub fn use_ascii(&self, s: &str) -> bool {
self.ascii && s.is_ascii()
}
pub fn compile(&self, pattern: &str) -> TestCompiledRegex {
self.compilef(pattern, "")
}
pub fn compilef(&self, pattern: &str, flags_str: &str) -> TestCompiledRegex {
let mut flags = regress::Flags::from(flags_str);
flags.no_opt = !self.optimize;
let re = regress::Regex::with_flags(pattern, flags);
assert!(
re.is_ok(),
"Failed to parse! flags: {} pattern: {}, error: {}",
flags_str,
pattern,
re.unwrap_err()
);
TestCompiledRegex {
re: re.unwrap(),
tc: *self,
}
}
pub fn test_match_succeeds(&self, pattern: &str, flags_str: &str, input: &str) {
let cr = self.compilef(pattern, flags_str);
cr.test_succeeds(input)
}
pub fn test_match_fails(&self, pattern: &str, flags_str: &str, input: &str) {
let cr = self.compilef(pattern, flags_str);
cr.test_fails(input)
}
}
pub fn test_with_configs<F>(func: F)
where
F: Fn(TestConfig),
{
func(TestConfig {
ascii: true,
optimize: false,
backend: Backend::PikeVM,
});
func(TestConfig {
ascii: false,
optimize: false,
backend: Backend::PikeVM,
});
func(TestConfig {
ascii: true,
optimize: false,
backend: Backend::Backtracking,
});
func(TestConfig {
ascii: false,
optimize: false,
backend: Backend::Backtracking,
});
func(TestConfig {
ascii: true,
optimize: true,
backend: Backend::Backtracking,
});
func(TestConfig {
ascii: false,
optimize: true,
backend: Backend::Backtracking,
});
}