use regex::Regex;
pub fn drop_nested(text: &str, open_re: &Regex, close_re: &Regex) -> String {
let mut spans: Vec<(usize, usize)> = Vec::new();
let mut nest = 0i64;
let Some(mut start) = open_re.find(text) else {
return text.to_string();
};
let mut end = close_re.find_at(text, start.end());
let mut next = start;
while let Some(mut e) = end {
let Some(nxt) = open_re.find_at(text, next.end()) else {
while nest > 0 {
nest -= 1;
match close_re.find_at(text, e.end()) {
Some(e0) => e = e0,
None => break,
}
}
spans.push((start.start(), e.end()));
break;
};
next = nxt;
let mut new_end = Some(e);
while e.end() < next.start() {
if nest != 0 {
nest -= 1;
let last = e.end();
match close_re.find_at(text, e.end()) {
Some(e2) => {
e = e2;
new_end = Some(e2);
}
None => {
let span = match spans.first() {
Some(&(s0, _)) => (s0, last),
None => (start.start(), last),
};
spans.clear();
spans.push(span);
break;
}
}
} else {
spans.push((start.start(), e.end()));
start = next;
new_end = close_re.find_at(text, next.end());
break; }
}
if next.range() != start.range() {
nest += 1;
}
end = new_end;
}
drop_spans(spans, text)
}
pub fn drop_spans(mut spans: Vec<(usize, usize)>, text: &str) -> String {
spans.sort_unstable();
let mut result = String::with_capacity(text.len());
let mut offset = 0;
for (s, e) in spans {
if offset <= s {
if offset < s {
result.push_str(&text[offset..s]);
}
offset = e;
}
}
result.push_str(&text[offset..]);
result
}
#[cfg(test)]
mod tests {
use std::sync::LazyLock;
use super::*;
static OPEN_TABLE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?i)\{\|").unwrap());
static CLOSE_TABLE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?i)\|\}").unwrap());
#[test]
fn drops_simple_table() {
assert_eq!(
drop_nested("before {| a |- b |} after", &OPEN_TABLE, &CLOSE_TABLE),
"before after"
);
}
#[test]
fn drops_nested_tables() {
assert_eq!(
drop_nested(
"x {| outer {| inner |} more |} y",
&OPEN_TABLE,
&CLOSE_TABLE
),
"x y"
);
}
#[test]
fn drops_consecutive_tables() {
assert_eq!(
drop_nested("a {| one |} b {| two |} c", &OPEN_TABLE, &CLOSE_TABLE),
"a b c"
);
}
#[test]
fn unbalanced_open_drops_to_last_close() {
assert_eq!(
drop_nested("a {| x {| y |} b", &OPEN_TABLE, &CLOSE_TABLE),
"a b"
);
}
#[test]
fn no_delimiters_returns_input() {
assert_eq!(
drop_nested("plain text", &OPEN_TABLE, &CLOSE_TABLE),
"plain text"
);
}
#[test]
fn drop_spans_skips_nested_spans() {
assert_eq!(
drop_spans(vec![(2, 8), (4, 6), (10, 12)], "0123456789abcdef"),
"0189cdef"
);
}
}