pub(crate) fn find_tag_close(input: &str) -> Option<usize> {
let bytes = input.as_bytes();
let mut quote: u8 = 0;
let mut i = 0;
while i < bytes.len() {
let c = bytes[i];
if quote != 0 {
if c == quote {
quote = 0;
}
} else {
match c {
b'>' => return Some(i),
b'"' | b'\'' => quote = c,
_ => {}
}
}
i += 1;
}
None
}
pub(crate) fn opening_tag_has_runtime_prefix(input: &str) -> bool {
let bytes = input.as_bytes();
let mut i = 0;
let mut quote: u8 = 0;
let mut prev_ws = false;
while i < bytes.len() {
let c = bytes[i];
if quote != 0 {
if c == quote {
quote = 0;
}
i += 1;
continue;
}
match c {
b'>' => return false,
b' ' | b'\t' | b'\n' | b'\r' => prev_ws = true,
b'"' | b'\'' => {
quote = c;
prev_ws = false;
}
b'@' | b':' | b'?' if prev_ws => return true,
_ => prev_ws = false,
}
i += 1;
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_tag_close_simple() {
assert_eq!(find_tag_close("<br>"), Some(3));
assert_eq!(find_tag_close("<br/>"), Some(4));
assert_eq!(find_tag_close("<template>"), Some(9));
}
#[test]
fn find_tag_close_skips_double_quoted_gt() {
assert_eq!(find_tag_close(r#"<if condition="a > b">"#), Some(21));
assert_eq!(find_tag_close(r#"<if condition="a >= b">"#), Some(22));
}
#[test]
fn find_tag_close_skips_single_quoted_gt() {
assert_eq!(find_tag_close(r#"<a x='a>b'>"#), Some(10));
}
#[test]
fn find_tag_close_unterminated_returns_none() {
assert_eq!(find_tag_close("<unterminated"), None);
assert_eq!(find_tag_close(r#"<a x="never closed"#), None);
}
#[test]
fn runtime_prefix_detects_event() {
assert!(opening_tag_has_runtime_prefix("<template @click={fn}>"));
}
#[test]
fn runtime_prefix_detects_bind() {
assert!(opening_tag_has_runtime_prefix(r#"<template :bar="baz">"#));
}
#[test]
fn runtime_prefix_detects_optional() {
assert!(opening_tag_has_runtime_prefix(r#"<template ?bool="true">"#));
}
#[test]
fn runtime_prefix_ignores_quoted_prefix_char() {
assert!(!opening_tag_has_runtime_prefix(
r#"<template href=":colon">"#
));
assert!(!opening_tag_has_runtime_prefix(
r#"<template href="mailto:x@y">"#
));
assert!(!opening_tag_has_runtime_prefix(r#"<template href="?q=1">"#));
}
#[test]
fn runtime_prefix_ignores_tag_name() {
assert!(!opening_tag_has_runtime_prefix("<template>"));
assert!(!opening_tag_has_runtime_prefix(r#"<template foo="bar">"#));
}
#[test]
fn runtime_prefix_stops_at_unquoted_gt() {
assert!(!opening_tag_has_runtime_prefix(
r#"<template><span @click="x"></span></template>"#
));
}
}