use alloc::string::String;
pub fn normalize_identifier(value: &str) -> String {
let mut result = String::with_capacity(value.len());
let bytes = value.as_bytes();
let mut in_whitespace = true;
let mut index = 0;
let mut start = 0;
while index < bytes.len() {
if matches!(bytes[index], b'\t' | b'\n' | b'\r' | b' ') {
if !in_whitespace {
result.push_str(&value[start..index]);
in_whitespace = true;
}
}
else if in_whitespace {
if !result.is_empty() {
result.push(' ');
}
start = index;
in_whitespace = false;
}
index += 1;
}
if !in_whitespace {
result.push_str(&value[start..]);
}
result.to_lowercase().to_uppercase()
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
#[test]
fn test_normalize_identifier_keeps_brackets() {
for code in 0..=0x10_FFFF_u32 {
if let Some(char) = char::from_u32(code) {
if matches!(char, '[' | ']' | '\\') {
continue;
}
let folded = char.to_string().to_lowercase().to_uppercase();
assert!(
!folded.contains(['[', ']', '\\']),
"should not fold {:?} into a bracket or backslash",
char
);
}
}
}
use super::*;
#[test]
fn test_normalize_identifier() {
assert_eq!(normalize_identifier("a b"), "A B", "should keep a space between words");
assert_eq!(
normalize_identifier(" a b "),
"A B",
"should collapse and trim whitespace"
);
assert_eq!(
normalize_identifier("a\t\r\nb"),
"A B",
"should collapse markdown whitespace"
);
assert_eq!(normalize_identifier("ẞ"), "SS", "should fold case");
}
}