use std::borrow::Cow;
use irgx::{Captures, NoExpand, Regex, RegexBuilder};
#[test]
fn find_and_iterate() {
let re = Regex::new(r"\d+").unwrap();
let text = "a1 bb22 c333";
let first = re.find(text).unwrap();
assert_eq!(first.as_str(), "1");
assert_eq!(first.start(), 1);
assert_eq!(first.end(), 2);
assert_eq!(first.range(), 1..2);
assert_eq!(first.len(), 1);
assert!(!first.is_empty());
assert_eq!(first.as_bytes(), b"1");
assert_eq!(
re.find_iter(text).map(|m| m.as_str()).collect::<Vec<_>>(),
["1", "22", "333"]
);
assert!(re.find("none here").is_none());
assert!(re.is_match(text));
assert!(!re.is_match("none here"));
}
#[test]
fn iteration_knows_its_shape() {
let re = Regex::new(r"\w+").unwrap();
let found = re.find_iter("one two three");
assert_eq!(found.len(), 3);
assert_eq!(
found.rev().map(|m| m.as_str()).collect::<Vec<_>>(),
["three", "two", "one"]
);
let mut once = Regex::new("a").unwrap().find_iter("a");
assert!(once.next().is_some());
assert!(once.next().is_none());
assert!(once.next().is_none());
}
#[test]
fn split_and_splitn() {
let re = Regex::new(r"\s*,\s*").unwrap();
assert_eq!(
re.split("a, b ,c, d").collect::<Vec<_>>(),
["a", "b", "c", "d"]
);
assert_eq!(re.split(",a,").collect::<Vec<_>>(), ["", "a", ""]);
assert_eq!(
re.split("no separators").collect::<Vec<_>>(),
["no separators"]
);
assert_eq!(re.splitn("a,b,c,d", 2).collect::<Vec<_>>(), ["a", "b,c,d"]);
assert_eq!(re.splitn("a,b,c,d", 1).collect::<Vec<_>>(), ["a,b,c,d"]);
assert_eq!(re.splitn("a,b,c,d", 0).count(), 0);
assert_eq!(re.splitn("a,b", 9).collect::<Vec<_>>(), ["a", "b"]);
}
#[test]
fn replace_family() {
let re = Regex::new(r"\d+").unwrap();
assert_eq!(re.replace("a1 b2 c3", "N"), "aN b2 c3");
assert_eq!(re.replace_all("a1 b2 c3", "N"), "aN bN cN");
assert_eq!(re.replacen("a1 b2 c3", 2, "N"), "aN bN c3");
assert_eq!(re.replacen("a1 b2 c3", 0, "N"), "aN bN cN");
let untouched = re.replace_all("nothing", "N");
assert!(matches!(untouched, Cow::Borrowed("nothing")));
}
#[test]
fn replacement_expands_group_references() {
let re = Regex::new(r"(?P<first>\w+)\s+(?P<last>\w+)").unwrap();
assert_eq!(re.replace("Ada Lovelace", "$last, $first"), "Lovelace, Ada");
assert_eq!(re.replace("Ada Lovelace", "$2 $1"), "Lovelace Ada");
assert_eq!(re.replace("Ada Lovelace", "${last}!"), "Lovelace!");
assert_eq!(re.replace("Ada Lovelace", "$$$first"), "$Ada");
assert_eq!(re.replace("Ada Lovelace", "[$nope]"), "[]");
assert_eq!(re.replace("Ada Lovelace", "x$"), "x$");
assert_eq!(
re.replace("Ada Lovelace", NoExpand("$1 costs $5")),
"$1 costs $5"
);
}
#[test]
fn replacement_can_be_a_closure() {
let re = Regex::new(r"\d+").unwrap();
let doubled = re.replace_all("a1 b20 c300", |caps: &Captures<'_, '_>| {
let n: u32 = caps[0].parse().unwrap();
(n * 2).to_string()
});
assert_eq!(doubled, "a2 b40 c600");
}
#[test]
fn replacement_is_byte_correct() {
let re = RegexBuilder::new("café").ignore_case(true).build().unwrap();
assert_eq!(re.replace_all("le café, le CAFÉ", "tea"), "le tea, le tea");
let re = Regex::new("é").unwrap();
assert_eq!(re.replace_all("café au naïf", "e"), "cafe au naïf");
}
#[test]
fn replacement_handles_empty_matches() {
let re = Regex::new("").unwrap();
assert_eq!(re.replace_all("abc", "-"), "-a-b-c-");
let star = Regex::new("a*").unwrap();
assert_eq!(star.replace_all("abc", "[]"), "[]b[]c[]");
}
#[test]
fn accessors_and_traits() {
let re: Regex = "a+b".parse().unwrap();
assert_eq!(re.as_str(), "a+b");
assert_eq!(re.to_string(), "a+b");
assert_eq!(format!("{re:?}"), "\"a+b\"");
assert_eq!(re.groups(), Some(0));
let cased = RegexBuilder::new("abc").ignore_case(true).build().unwrap();
let copy = cased.clone();
assert_eq!(copy.as_str(), "abc");
assert!(copy.is_match("ABC"));
assert_eq!(copy.find_iter("ABC abc").count(), 2);
assert_eq!(irgx::ABI_VERSION, 2);
assert!(!irgx::engine_version().is_empty());
assert!(!irgx::pcre2_version().is_empty());
}
#[test]
fn checked_verbs_mirror_the_panicking_ones() {
let re = Regex::new(r"(\w)(\d)").unwrap();
let text = "a1 b2";
assert_eq!(re.try_is_match(text).unwrap(), re.is_match(text));
assert_eq!(
re.try_find(text).unwrap().map(|m| m.range()),
re.find(text).map(|m| m.range())
);
assert_eq!(
re.try_find_iter(text).unwrap().count(),
re.find_iter(text).count()
);
assert_eq!(
re.try_captures(text).unwrap().map(|c| c[1].to_owned()),
re.captures(text).map(|c| c[1].to_owned())
);
assert_eq!(
re.try_captures_iter(text).unwrap().count(),
re.captures_iter(text).count()
);
assert_eq!(
re.try_replacen(text, usize::MAX, "x").unwrap(),
re.replace_all(text, "x")
);
}
#[test]
fn many_matches_are_not_truncated() {
let text = "a ".repeat(20_000);
let re = Regex::new("a").unwrap();
assert_eq!(re.find_iter(&text).count(), 20_000);
assert_eq!(
re.find_iter(&text).next_back().unwrap().start(),
text.len() - 2
);
let empty = Regex::new("").unwrap();
assert_eq!(empty.find_iter(&text).count(), text.len() + 1);
let grouped = Regex::new("(a)").unwrap();
assert_eq!(grouped.captures_iter(&text).count(), 20_000);
}
#[test]
fn a_short_window_still_returns_every_match() {
const FIRST_WINDOW: usize = 4096;
let re = Regex::new("a").unwrap();
for count in [1, FIRST_WINDOW - 1, FIRST_WINDOW, FIRST_WINDOW + 1, 9_999] {
let text = "a.".repeat(count);
let found: Vec<_> = re.find_iter(&text).map(|m| m.start()).collect();
assert_eq!(found.len(), count, "{count} matches went in");
assert!(
found
.iter()
.enumerate()
.all(|(nth, start)| *start == nth * 2),
"the spans are not the ones the text has, at count {count}"
);
}
let text = "a.".repeat(FIRST_WINDOW + 1);
assert_eq!(re.split(&text).count(), FIRST_WINDOW + 2);
assert_eq!(re.replace_all(&text, "").len(), FIRST_WINDOW + 1);
}
#[test]
fn expand_writes_into_a_caller_buffer() {
let re = Regex::new(r"(?P<k>\w+)=(?P<v>\w+)").unwrap();
let mut out = String::new();
for caps in re.captures_iter("a=1 b=2") {
caps.expand("$k:$v ", &mut out);
}
assert_eq!(out, "a:1 b:2 ");
}
#[test]
fn captures_iter_and_len() {
let re = Regex::new(r"(a)(b)?").unwrap();
let caps = re.captures("ab").unwrap();
assert_eq!(caps.len(), 3);
assert_eq!(
caps.iter()
.map(|g| g.map(|m| m.as_str()))
.collect::<Vec<_>>(),
[Some("ab"), Some("a"), Some("b")]
);
let caps = re.captures("ac").unwrap();
assert_eq!(
caps.iter()
.map(|g| g.map(|m| m.as_str()))
.collect::<Vec<_>>(),
[Some("a"), Some("a"), None]
);
}