pub fn find_closing_tag_case_insensitive(haystack: &str, needle: &str) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
let needle_bytes = needle.as_bytes();
let haystack_bytes = haystack.as_bytes();
if needle_bytes.len() > haystack_bytes.len() {
return None;
}
for i in 0..=haystack_bytes.len() - needle_bytes.len() {
let mut matches = true;
for j in 0..needle_bytes.len() {
let h_byte = haystack_bytes[i + j];
let n_byte = needle_bytes[j];
let h_lower = if h_byte >= b'A' && h_byte <= b'Z' {
h_byte + 32 } else {
h_byte
};
let n_lower = if n_byte >= b'A' && n_byte <= b'Z' {
n_byte + 32 } else {
n_byte
};
if h_lower != n_lower {
matches = false;
break;
}
}
if matches {
return Some(i);
}
}
None
}
pub fn strip_trailing_self_close_optimized(s: &str) -> (String, bool) {
if s.is_empty() {
return (s.to_string(), false);
}
let bytes = s.as_bytes();
let mut idx = bytes.len();
while idx > 0 && bytes[idx - 1].is_ascii_whitespace() {
idx -= 1;
}
if idx > 0 && bytes[idx - 1] == b'/' {
if !s.contains('"') && !s.contains('\'') {
let cleaned = s[..idx - 1].to_string();
return (cleaned, true);
}
let mut in_single = false;
let mut in_double = false;
for i in 0..(idx - 1) {
match bytes[i] {
b'"' if !in_single => in_double = !in_double,
b'\'' if !in_double => in_single = !in_single,
_ => {}
}
}
if !in_single && !in_double {
let cleaned = s[..idx - 1].to_string();
return (cleaned, true);
}
}
(s.to_string(), false)
}