use super::go_blocks::extract_block_parts;
use super::static_regex;
use regex::Regex;
use std::ops::Range;
use std::sync::LazyLock;
static GO_BLOCK_RE: LazyLock<Regex> = LazyLock::new(|| static_regex(r"(?s)\{\{.*?\}\}|\{%.*?%\}"));
pub(crate) fn raw_spans(template: &str) -> Vec<Range<usize>> {
if !template.contains("raw") {
return Vec::new();
}
let mut spans = Vec::new();
let mut open: Option<usize> = None;
for m in GO_BLOCK_RE.find_iter(template) {
if !m.as_str().starts_with("{%") {
continue;
}
let (_, inner, _) = extract_block_parts(m.as_str());
match inner.trim() {
"raw" if open.is_none() => open = Some(m.start()),
"endraw" => {
if let Some(start) = open.take() {
spans.push(start..m.end());
}
}
_ => {}
}
}
if let Some(start) = open {
spans.push(start..template.len());
}
spans
}
pub(crate) fn raw_span_at(spans: &[Range<usize>], at: usize) -> Option<&Range<usize>> {
spans.iter().find(|span| span.contains(&at))
}
pub(super) fn replace_live_blocks(
template: &str,
mut rewrite: impl FnMut(&str) -> String,
) -> String {
let spans = raw_spans(template);
let mut out = String::with_capacity(template.len());
let mut copied = 0;
for m in GO_BLOCK_RE.find_iter(template) {
if raw_span_at(&spans, m.start()).is_some() {
continue;
}
out.push_str(&template[copied..m.start()]);
out.push_str(&rewrite(m.as_str()));
copied = m.end();
}
out.push_str(&template[copied..]);
out
}
pub(super) fn live_blocks(template: &str) -> impl Iterator<Item = &str> {
let spans = raw_spans(template);
GO_BLOCK_RE
.find_iter(template)
.filter(move |m| raw_span_at(&spans, m.start()).is_none())
.map(|m| m.as_str())
}