const PLACEHOLDER_PREFIX: &str = "{# __askama_fmt_internal_skip_";
const PLACEHOLDER_SUFFIX: &str = "__ #}";
#[derive(Debug, PartialEq, Eq)]
enum Directive {
SkipFile,
Off,
On,
}
struct DirectiveSpan {
start: usize,
end: usize,
kind: Directive,
}
pub fn has_skip_file(input: &str) -> bool {
scan_directives(input).any(|d| d.kind == Directive::SkipFile)
}
pub fn extract_regions(input: &str) -> (String, Vec<(String, String)>) {
let directives: Vec<DirectiveSpan> = scan_directives(input).collect();
if directives.is_empty() {
return (input.to_string(), Vec::new());
}
let mut regions: Vec<(String, String)> = Vec::new();
let mut out = String::with_capacity(input.len());
let mut cursor = 0usize;
let mut i = 0usize;
while i < directives.len() {
let d = &directives[i];
if d.kind != Directive::Off {
i += 1;
continue;
}
let on_idx = directives[i + 1..]
.iter()
.position(|x| x.kind == Directive::On)
.map(|p| p + i + 1);
let Some(on_idx) = on_idx else {
i += 1;
continue;
};
let on_end = directives[on_idx].end;
out.push_str(&input[cursor..d.start]);
let placeholder = format!(
"{}{}{}",
PLACEHOLDER_PREFIX,
regions.len(),
PLACEHOLDER_SUFFIX
);
out.push_str(&placeholder);
let original = input[d.start..on_end].to_string();
regions.push((placeholder, original));
cursor = on_end;
i = on_idx + 1;
}
out.push_str(&input[cursor..]);
(out, regions)
}
pub fn restore_regions(formatted: &str, regions: &[(String, String)]) -> String {
if regions.is_empty() {
return formatted.to_string();
}
let mut out = formatted.to_string();
for (placeholder, original) in regions {
if let Some(pos) = out.find(placeholder.as_str()) {
out.replace_range(pos..pos + placeholder.len(), original);
}
}
out
}
fn scan_directives(input: &str) -> DirectiveIter<'_> {
DirectiveIter { input, pos: 0 }
}
struct DirectiveIter<'a> {
input: &'a str,
pos: usize,
}
impl Iterator for DirectiveIter<'_> {
type Item = DirectiveSpan;
fn next(&mut self) -> Option<DirectiveSpan> {
let bytes = self.input.as_bytes();
while self.pos + 1 < bytes.len() {
if bytes[self.pos] == b'{' && bytes[self.pos + 1] == b'#' {
let start = self.pos;
let after = start + 2;
match self.input[after..].find("#}") {
Some(rel) => {
let inner = &self.input[after..after + rel];
let end = after + rel + 2;
self.pos = end;
if let Some(kind) = parse_directive(inner) {
return Some(DirectiveSpan { start, end, kind });
}
}
None => {
self.pos = bytes.len();
return None;
}
}
continue;
}
self.pos += 1;
}
None
}
}
fn parse_directive(inner: &str) -> Option<Directive> {
let s = inner
.trim()
.trim_start_matches(['-', '+', '~'])
.trim_start()
.trim_end_matches(['-', '+', '~'])
.trim();
let rest = s
.strip_prefix("askama_fmt")?
.trim_start()
.strip_prefix(':')?
.trim();
match rest {
"off" => Some(Directive::Off),
"on" => Some(Directive::On),
"skip-file" => Some(Directive::SkipFile),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_off() {
assert_eq!(parse_directive(" askama_fmt: off "), Some(Directive::Off));
}
#[test]
fn parse_skip_file() {
assert_eq!(
parse_directive(" askama_fmt: skip-file "),
Some(Directive::SkipFile)
);
}
#[test]
fn parse_with_dash_whitespace_control() {
assert_eq!(parse_directive("- askama_fmt: off -"), Some(Directive::Off));
}
#[test]
fn has_skip_file_basic() {
assert!(has_skip_file("{# askama_fmt: skip-file #}\n<div></div>"));
}
#[test]
fn extract_simple_region() {
let input = "before\n{# askama_fmt: off #}\nraw\n{# askama_fmt: on #}\nafter\n";
let (stripped, regions) = extract_regions(input);
assert_eq!(regions.len(), 1);
assert!(stripped.contains("__askama_fmt_internal_skip_0__"));
let restored = restore_regions(&stripped, ®ions);
assert_eq!(restored, input);
}
}