pub const OPEN: &str = "{%";
pub const CLOSE: &str = "%}";
#[derive(Clone, Copy)]
enum State {
Normal,
String,
Escape,
}
#[must_use]
pub fn find_tag_end(content: &str, start: usize) -> Option<usize> {
let bytes = content.as_bytes();
if start > bytes.len() {
return None;
}
let mut state = State::Normal;
let mut pos = start;
while pos < bytes.len() {
let byte = *bytes.get(pos)?;
match state {
State::String => match byte {
b'"' => state = State::Normal,
b'\\' => state = State::Escape,
_ => {}
},
State::Escape => state = State::String,
State::Normal => {
if byte == b'"' {
state = State::String;
} else if bytes.get(pos..pos + CLOSE.len()) == Some(CLOSE.as_bytes()) {
return Some(pos);
}
}
}
pos += 1;
}
None
}
#[must_use]
pub fn contains_markdoc_tag_in_url(content: &str, protocols: &[&str]) -> bool {
let bytes = content.as_bytes();
let mut pos = 0;
while pos < bytes.len() {
if bytes.get(pos..pos + OPEN.len()) != Some(OPEN.as_bytes()) {
pos += 1;
continue;
}
if find_tag_end(content, pos).is_none() {
pos += OPEN.len();
continue;
}
let mut start = pos;
while start > 0 && !bytes.get(start - 1).is_some_and(u8::is_ascii_whitespace) {
start -= 1;
}
let prefix = content.get(start..pos).unwrap_or("");
return protocols
.iter()
.any(|protocol| prefix.contains(&format!("{protocol}://")));
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn finds_the_closing_delimiter() {
assert_eq!(find_tag_end("{% foo %}", 0), Some(7));
assert_eq!(find_tag_end("a {% foo %} b", 2), Some(9));
}
#[test]
fn a_closing_delimiter_inside_a_string_does_not_close_the_tag() {
let content = r#"{% foo bar="100%}" %}"#;
assert_eq!(find_tag_end(content, 0), Some(19));
}
#[test]
fn an_escaped_quote_does_not_end_the_string() {
let content = r#"{% foo bar="a\"%}b" %}"#;
assert_eq!(find_tag_end(content, 0), Some(20));
}
#[test]
fn an_unclosed_tag_is_not_an_error() {
assert_eq!(find_tag_end("{% foo", 0), None);
assert_eq!(find_tag_end("{% foo \"unterminated %}", 0), None);
}
#[test]
fn a_start_past_the_end_yields_none_rather_than_panicking() {
assert_eq!(find_tag_end("{% %}", 99), None);
}
#[test]
fn urls_carrying_tags_are_detected() {
let http = ["http", "https"];
assert!(!contains_markdoc_tag_in_url(
"The link is https://example.com. {% tag /%})",
&http
));
assert!(!contains_markdoc_tag_in_url("[Link]({% tag %})", &http));
assert!(contains_markdoc_tag_in_url(
"https://example.com/{% tag %}content{% /tag %})",
&http
));
assert!(contains_markdoc_tag_in_url(
"https://en.wikipedia.org/wiki/Exam_(disambiguation){% tag /%}",
&http
));
assert!(contains_markdoc_tag_in_url(
"[Link](https://{% $variable.custom_value %})",
&http
));
assert!(contains_markdoc_tag_in_url(
"[Link](https://example.com/{% tag /%})",
&http
));
assert!(contains_markdoc_tag_in_url(
"[Link](https://example.com/{% tag %}content{% /tag %})",
&http
));
}
#[test]
fn the_protocol_list_is_the_callers() {
assert!(contains_markdoc_tag_in_url(
"[Link](vscode://{% $variable.custom_value %})",
&["vscode"]
));
assert!(!contains_markdoc_tag_in_url(
"[Link](vscode://{% $variable.custom_value %})",
&["http", "https"]
));
}
#[test]
fn an_unclosed_tag_in_a_url_is_stepped_over() {
assert!(!contains_markdoc_tag_in_url(
"https://example.com/{% unclosed",
&["https"]
));
}
}