use std::collections::HashMap;
use std::sync::LazyLock;
static HTML_ENTITIES: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
let mut m = HashMap::new();
m.insert("©", "©");
m.insert("™", "™");
m.insert("®", "®");
m.insert("&", "&");
m.insert("<", "<");
m.insert(">", ">");
m.insert(""", "\"");
m.insert("'", "'");
m.insert(" ", " ");
m.insert("—", "—");
m.insert("–", "–");
m.insert("…", "…");
m.insert("←", "←");
m.insert("→", "→");
m.insert("↑", "↑");
m.insert("↓", "↓");
m.insert("×", "×");
m.insert("÷", "÷");
m.insert("±", "±");
m.insert("≠", "≠");
m.insert("≤", "≤");
m.insert("≥", "≥");
m.insert("∞", "∞");
m.insert("€", "€");
m.insert("£", "£");
m.insert("¥", "¥");
m.insert("¢", "¢");
m.insert("°", "°");
m.insert("¶", "¶");
m.insert("§", "§");
m.insert("•", "•");
m.insert("·", "·");
m.insert("«", "«");
m.insert("»", "»");
m.insert("†", "†");
m.insert("‡", "‡");
m.insert("‰", "‰");
m.insert("′", "′");
m.insert("″", "″");
m
});
pub fn decode_html_entities(text: &str) -> String {
let mut result = text.to_string();
for (entity, replacement) in HTML_ENTITIES.iter() {
result = result.replace(entity, replacement);
}
while let Some(start) = result.find("&#") {
if let Some(end) = result[start..].find(';') {
let entity = &result[start..start + end + 1];
let num_str = &entity[2..entity.len() - 1];
let codepoint = if num_str.starts_with('x') || num_str.starts_with('X') {
u32::from_str_radix(&num_str[1..], 16).ok()
} else {
num_str.parse::<u32>().ok()
};
if let Some(cp) = codepoint {
if let Some(c) = char::from_u32(cp) {
result = result.replace(entity, &c.to_string());
continue;
}
}
}
break;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_named_entities() {
assert_eq!(decode_html_entities("©"), "©");
assert_eq!(decode_html_entities("™"), "™");
assert_eq!(decode_html_entities("®"), "®");
assert_eq!(decode_html_entities("&"), "&");
}
#[test]
fn test_numeric_entities() {
assert_eq!(decode_html_entities("©"), "©");
assert_eq!(decode_html_entities("©"), "©");
}
#[test]
fn test_mixed() {
assert_eq!(
decode_html_entities("Copyright © 2024"),
"Copyright © 2024"
);
}
}