pub fn parse_id_class_attrs_fast(attr_part: &str) -> (Vec<(String, String)>, bool) {
let mut attrs = Vec::with_capacity(4);
let mut saw_other_attr = false;
if attr_part.is_empty() {
return (attrs, saw_other_attr);
}
let bytes = attr_part.as_bytes();
let mut i = 0;
while i < bytes.len() {
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i >= bytes.len() {
break;
}
let name_start = i;
while i < bytes.len() {
let b = bytes[i];
if b.is_ascii_alphanumeric()
|| b == b'_' || b == b':'
|| b == b'.' || b == b'-'
|| b == b'(' || b == b')'
|| b == b'[' || b == b']'
|| b == b'#' || b == b'@'
|| b == b'$' || b == b'?'
{
i += 1;
} else {
break;
}
}
if name_start == i {
i += 1;
continue;
}
let attr_name = &attr_part[name_start..i];
let lower_name = attr_name.to_lowercase();
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
if i < bytes.len() && bytes[i] == b'=' {
i += 1;
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
}
let value = if i < bytes.len() {
if bytes[i] == b'"' {
i += 1;
let value_start = i;
while i < bytes.len() && bytes[i] != b'"' {
i += 1;
}
let value = &attr_part[value_start..i];
if i < bytes.len() {
i += 1;
} value
} else if bytes[i] == b'\'' {
i += 1;
let value_start = i;
while i < bytes.len() && bytes[i] != b'\'' {
i += 1;
}
let value = &attr_part[value_start..i];
if i < bytes.len() {
i += 1;
} value
} else {
let value_start = i;
while i < bytes.len() && !bytes[i].is_ascii_whitespace() && bytes[i] != b'>' {
i += 1;
}
&attr_part[value_start..i]
}
} else {
""
};
if lower_name == "id" || lower_name == "class" {
let decoded = html_escape::decode_html_entities(value);
attrs.push((lower_name, decoded.into_owned()));
} else {
saw_other_attr = true;
}
} else {
if lower_name == "id" || lower_name == "class" {
attrs.push((lower_name, String::new()));
} else {
saw_other_attr = true;
}
}
}
(attrs, saw_other_attr)
}