#![allow(
clippy::unwrap_used,
clippy::panic,
clippy::expect_used,
unused_must_use,
clippy::pedantic
)]
use std::borrow::Cow;
use crate::{DEFAULT_REGEX_CONF, Regex, RegexConf, RegexTestable, ReplaceRegex};
macro_rules! template_with_conf {
($r:expr, $c:expr, $must_pass:expr, $must_fail:expr $(,)?) => {
let must_fail: &[&str] = $must_fail;
let regex = Regex::compile($r).expect("Regex failed to compile");
for mp in $must_pass {
if !regex.test_with_conf(mp, $c) {
panic!("Should've passed: \"{mp}\"");
}
}
for mf in must_fail {
if regex.test_with_conf(mf, $c) {
panic!("Should've failed: \"{mf}\"");
}
}
};
}
macro_rules! template {
($r:expr, $must_pass:expr, $must_fail:expr $(,)?) => {
template_with_conf!($r, DEFAULT_REGEX_CONF, $must_pass, $must_fail)
};
}
#[test]
fn abc() {
template!(
"abc",
&["abc", "abcc", "aabc", "abcabc"],
&["ab", "a", "bc"],
);
let regex = Regex::compile("abc").unwrap();
assert_eq!(2, regex.find_matches("abcabc").count());
}
#[test]
fn dot() {
template!(
"a..d",
&["abcd", "a..d"],
&["ad", "abd", "abcc", "aabc", "...."],
);
}
#[test]
fn or() {
template!("(abc|cba)", &["abc", "cba", "babc", "aabc"], &["cga"]);
}
#[test]
fn opt() {
template!(
"head(opt-body)?tail",
&["headtail", "headopt-bodytail"],
&["headopt-body", "opt-bodytail"],
);
template!("a.?b", &["ab", "acb"], &["accb", "ac"]);
}
#[test]
fn star() {
template!("a(abc)*c", &["aabcc", "ac", "aabcabcc"], &["abbc"]);
template!(".*", &["", "daksd"], &[]);
template!(
"a(1.*2)b",
&["a12b", "a1salk2b", "a112212b"],
&["a1b", "a22b"],
);
template!(
"a(1.*2)b \\1$",
&["a12b 12", "a1salk2b 1salk2", "a112212b 112212"],
&["a1b", "a22b"],
);
}
#[test]
fn repeat_star() {
template!(
"a(1.*2){2,3}b \\1$",
&["a1212b 12", "a121212b 12", "a1uno21dos2b 1dos2"],
&[],
);
template!(
"(.+){5}",
&["aaaaa", "aaaaaa", "aaaaaaaa"],
&["a", "aa", "aaa", "aaaa"],
);
}
#[test]
fn plus() {
template!("a+bc", &["abc", "aabc", "aaaabc", "ababc"], &["bc", "bbc"]);
}
#[test]
fn start_end() {
template!("abc", &["abc", "aabc", "abcc"], &[]);
template!("^abc", &["abc", "abcc"], &["aabc"]);
template!("abc$", &["abc", "aabc"], &["abcc"]);
template!("^abc$", &["abc"], &["aabc", "abcc"]);
}
#[test]
fn trait_test() {
assert!("a??bbbc".matches_regex("a..b+c"));
assert!(!"abc".matches_regex("a\\.c"));
}
#[test]
fn nested() {
template!(
"abc((dfg)+|(hij)+)?klm",
&["abcdfgklm", "abcklm", "abcdfgklm"],
&["abcdfghijklm"],
);
}
#[test]
fn fail() {
for c in ["?", "*", "+"] {
let msg = format!("Expected pattern before '{c}'");
match Regex::compile(c) {
Ok(_) => panic!(),
Err(err) => assert_eq!(err.to_string(), msg),
}
}
}
#[test]
fn find_matches() {
let pattern = "A(bc)*D";
let regex = Regex::compile(pattern).unwrap();
let mut matches = regex.find_matches("AD_AD");
let m = matches.next().unwrap();
assert_eq!((0, 2), m.span());
assert_eq!(2, m.slice().len());
assert_eq!("AD", m.slice());
let m = matches.next().unwrap();
assert_eq!((3, 5), m.span());
assert_eq!(2, m.slice().len());
assert_eq!("AD", m.slice());
let pattern = "";
let regex = Regex::compile(pattern).unwrap();
let mut matches = regex.find_matches("AD");
let m = matches.next().unwrap();
assert_eq!((0, 0), m.span());
assert_eq!(0, m.slice().len());
assert_eq!("", m.slice());
let m = matches.next().unwrap();
assert_eq!((1, 1), m.span());
assert_eq!(0, m.slice().len());
assert_eq!("", m.slice());
assert!(matches.next().is_none());
}
#[test]
fn range() {
template!(
"^[a-z01]+$",
&["avcd", "0101baba1"],
&["avcdZZka", "0101baba91"],
);
template!(
"^[^a-z01]+$",
&["99882"],
&["avcd", "0101baba1", "avcdZZka", "0101baba91"],
);
}
#[test]
fn min_max() {
template!(
"^a{3,5}$",
&["aaa", "aaaa", "aaaaa"],
&["a", "aa", "aaaaaa", "aaaaaaa"],
);
template!(
"^a{6}$",
&["aaaaaa"],
&["a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaaa", "aaaaaaaa"],
);
template!(
"^a{3,}$",
&["aaa", "aaaa", "aaaaa", "aaaaaa", "aaaaaaa"],
&["a", "aa"],
);
template!(
"^a{,5}$",
&["a", "aa", "aaa", "aaaa", "aaaaa"],
&["aaaaaa", "aaaaaaa"],
);
}
#[test]
fn lazy() {
let regex = Regex::compile(".*?b").unwrap();
assert_eq!(2, regex.find_matches("aaaaaabaaaaaab").count());
let regex = Regex::compile(".*b").unwrap();
assert_eq!(1, regex.find_matches("aaaaaabaaaaaab").count());
let regex = Regex::compile(".+?b").unwrap();
assert_eq!(2, regex.find_matches("aaaaaabaaaaaab").count());
let regex = Regex::compile(".+b").unwrap();
assert_eq!(1, regex.find_matches("aaaaaabaaaaaab").count());
}
#[test]
fn capture() {
template!("^ab(.)c\\1$", &["ab1c1", "ab2c2"], &["ab1c2", "ab2c1"]);
template!(
"^ab( [a-z]* )c\\1$",
&["ab abcd c abcd ", "ab ahc c ahc "],
&[
"ab ahc c ahc",
"ab ahc cahc ",
"ab ahc cahc",
"ab ahcc ahc",
"ab ag2a c ag2a ",
"ab1c2",
"ab2c1",
],
);
template!(
"^1(.*?)2\\1(.*?)3\\k<2>4$",
&["1abc2abcdef3def4", "1abc2abc34"],
&["1abc2abcd34"],
);
}
#[test]
fn named_capture() {
template!(
"^ab(?<twoch>..)c\\k<twoch>$",
&["ab12c12"],
&["ab1c2", "ab2c1"],
);
template!("^ab(?<guion>-.+-)c\\k<guion>$", &["ab-123-c-123-"], &[]);
}
#[test]
fn capture_or() {
template!(
"^(abc|def)123\\1$",
&["abc123abc", "def123def"],
&["abc123def", "def123abc"],
);
}
#[test]
fn case_sensitive() {
template_with_conf!(
"abc[a-z]",
RegexConf {
case_sensitive: false,
ignore_captures_in_result: false,
},
&["abcz", "ABCz", "AbcZ", "abCZbABc"],
&["abz", "abdc"],
);
template_with_conf!(
"abc[a-z]",
RegexConf {
case_sensitive: true,
ignore_captures_in_result: false,
},
&["abcz", "abca"],
&["ABC", "Abc", "abcZ", "abCbABc", "ab", "abdc"],
);
}
#[test]
fn special_escape() {
template!(
"^\\w{2,10}@mail\\.com$",
&["ab@mail.com", "abcde@mail.com", "abc_defg@mail.com"],
&["a@mail.com", "abcdefghijklmnop@mail.com", "abc?s@mail.com"],
);
}
#[test]
fn replace_regex() {
let input = "abcdacb";
let replaced = input.replace_regex("a.?b", "0").unwrap();
assert!(matches!(replaced, Cow::Owned(_)));
assert_eq!(replaced, "0cd0");
let input = "abcd";
let replaced = input.replace_regex("[0-9]", "P").unwrap();
assert!(matches!(replaced, Cow::Borrowed(_)));
assert_eq!(replaced, input);
}