use crate::markup::entity::{DecodedEntity, EntityContext, decode_one};
use htmlize::Context;
pub fn try_decode_entity(input: &[u8], context: Context) -> Option<(char, usize)> {
let context = match context {
Context::General => EntityContext::Text,
Context::Attribute => EntityContext::Attribute,
};
let (value, consumed) = decode_one(input, context)?;
let first = match value {
DecodedEntity::Named(text) => text.chars().next()?,
DecodedEntity::Numeric(ch) => ch,
};
Some((first, consumed))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn general_named_semicolon() {
let s = b"&rest";
let (c, n) = try_decode_entity(s, Context::General).unwrap();
assert_eq!(c, '&');
assert_eq!(n, 5);
assert_eq!(&s[n..], b"rest");
}
#[test]
fn general_named_no_semicolon_longest() {
let s = b"×X";
let (c, n) = try_decode_entity(s, Context::General).unwrap();
assert_eq!(c, '\u{00d7}');
assert_eq!(n, 6);
}
#[test]
fn attribute_times_x_not_entity() {
assert!(try_decode_entity(b"×X", Context::Attribute).is_none());
}
#[test]
fn numeric_dec() {
let s = b"&z";
let (c, n) = try_decode_entity(s, Context::General).unwrap();
assert_eq!(c, '&');
assert_eq!(n, 5);
}
#[test]
fn double_ampersand_amp_from_start_is_none() {
assert!(try_decode_entity(b"&&", Context::General).is_none());
assert!(try_decode_entity(b"&&", Context::Attribute).is_none());
}
#[test]
fn double_ampersand_decode_second_reference() {
let s = b"&&";
let (c, n) = try_decode_entity(&s[1..], Context::General).unwrap();
assert_eq!(c, '&');
assert_eq!(n, 5);
assert_eq!(1 + n, s.len());
}
#[test]
fn hex_numeric() {
let s = b"&y";
let (c, n) = try_decode_entity(s, Context::General).unwrap();
assert_eq!(c, '&');
assert_eq!(n, 6);
assert_eq!(&s[n..], b"y");
}
#[test]
fn attribute_named_with_semicolon() {
let s = b"<";
let (c, n) = try_decode_entity(s, Context::Attribute).unwrap();
assert_eq!(c, '<');
assert_eq!(n, 4);
}
#[test]
fn numeric_surrogate_replaced() {
let s = b"�";
let (c, n) = try_decode_entity(s, Context::General).unwrap();
assert_eq!(c, '\u{FFFD}');
assert_eq!(n, 8);
}
#[test]
fn numeric_windows_1252_mapping() {
let s = b"€";
let (c, n) = try_decode_entity(s, Context::General).unwrap();
assert_eq!(c, '\u{20AC}');
assert_eq!(n, 6);
}
#[test]
fn bare_ampersand_is_none() {
assert!(try_decode_entity(b"&", Context::General).is_none());
assert!(try_decode_entity(b"&@", Context::General).is_none());
}
}