pub fn normalize_attr_quotes(attrs_with_leading_space: &str, force_normalize: bool) -> String {
let s = attrs_with_leading_space;
let bytes = s.as_bytes();
let mut i = 0;
let mut out = String::with_capacity(s.len() + 16);
while i < bytes.len() {
if bytes[i].is_ascii_whitespace() {
out.push(bytes[i] as char);
i += 1;
continue;
}
let key_start = i;
while i < bytes.len() {
let c = bytes[i] as char;
if c.is_ascii_alphanumeric() || matches!(c, '_' | ':' | '.' | '-') {
i += 1;
} else {
break;
}
}
if key_start == i {
out.push(bytes[i] as char);
i += 1;
continue;
}
let key = &s[key_start..i];
let mut j = i;
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
if j >= bytes.len() || bytes[j] != b'=' {
let ws_len = j - i;
out.push_str(key);
if ws_len > 0 && j < bytes.len() {
let next = bytes[j] as char;
if next != '>' && next != '/' {
out.push(' ');
}
}
i = j;
continue;
}
j += 1; while j < bytes.len() && bytes[j].is_ascii_whitespace() {
j += 1;
}
out.push_str(key);
out.push('=');
if j >= bytes.len() {
break;
}
let c = bytes[j] as char;
if c == '"' || c == '\'' {
j += 1;
let val_start = j;
while j < bytes.len() && bytes[j] as char != c {
j += 1;
}
let val = &s[val_start..j];
if force_normalize {
out.push('"');
if c == '\'' && val.contains('"') {
out.push_str(&val.replace('"', """));
} else {
out.push_str(val);
}
out.push('"');
} else {
out.push(c);
out.push_str(val);
out.push(c);
}
if j < bytes.len() {
j += 1; }
i = j;
continue;
} else {
let val_start = j;
while j < bytes.len() {
let cc = bytes[j] as char;
if cc.is_ascii_whitespace() || matches!(cc, '>' | '/' | '"' | '\'' | '=') {
break;
}
j += 1;
}
out.push('"');
out.push_str(&s[val_start..j]);
out.push('"');
i = j;
continue;
}
}
out
}