#[cfg(test)]
extern crate alloc;
#[cfg(test)]
use alloc::string::String;
use core::fmt::{Result, Write};
use oxiplate_traits::Escaper;
#[allow(non_camel_case_types)]
pub enum HtmlEscaper {
text,
attr,
comment,
}
impl Escaper for HtmlEscaper {
const DEFAULT: Self = Self::text;
#[inline]
fn escape<W: Write + ?Sized>(&self, f: &mut W, value: &str) -> Result {
match self {
Self::text => escape_text(f, value),
Self::attr => escape_attribute_quoted_value(f, value),
Self::comment => escape_comment_text(f, value),
}
}
}
#[inline]
pub fn escape_text<W: Write + ?Sized>(f: &mut W, value: &'_ str) -> Result {
if !value.contains(['&', '<']) {
return f.write_str(value);
}
for character in value.chars() {
match character {
'&' => f.write_str("&")?,
'<' => f.write_str("<")?,
_ => f.write_char(character)?,
}
}
Ok(())
}
#[inline]
pub fn escape_attribute_quoted_value<W: Write + ?Sized>(f: &mut W, value: &'_ str) -> Result {
if !value.contains(['&', '"', '\'']) {
return f.write_str(value);
}
for character in value.chars() {
match character {
'&' => f.write_str("&")?,
'"' => f.write_str(""")?,
'\'' => f.write_str("'")?,
_ => f.write_char(character)?,
}
}
Ok(())
}
#[inline]
pub fn escape_comment_text<W: Write + ?Sized>(f: &mut W, value: &'_ str) -> Result {
if
!value.starts_with('>')
&& !value.starts_with('-')
&& !value.contains("--")
&& !value.ends_with("<!-")
&& !value.ends_with('-')
{
return f.write_str(value);
}
for character in value.chars() {
match character {
'-' => f.write_char('−')?,
'!' => f.write_char('ǃ')?,
'<' => f.write_char('‹')?,
'>' => f.write_char('›')?,
_ => f.write_char(character)?,
}
}
Ok(())
}
#[test]
fn test_escape_attribute_quoted_value() {
let mut string = String::new();
escape_attribute_quoted_value(&mut string, r#"&"' hello"#).unwrap();
assert_eq!("&"' hello", string);
}
#[test]
fn test_escape_attribute_quoted_value_nothing_to_escape() {
let mut string = String::new();
escape_attribute_quoted_value(&mut string, r#"Hello world!"#).unwrap();
assert_eq!("Hello world!", string);
}