use pretty::{Arena, DocAllocator, DocBuilder};
use std::borrow::Cow;
pub(crate) fn escape(value: &str) -> Cow<'_, str> {
let first = value
.bytes()
.position(|b| matches!(b, b'&' | b'<' | b'>' | b'"' | b'\''));
let Some(first) = first else {
return Cow::Borrowed(value);
};
let mut escaped = String::with_capacity(value.len() + 8);
escaped.push_str(&value[..first]);
for c in value[first..].chars() {
match c {
'&' => escaped.push_str("&"),
'<' => escaped.push_str("<"),
'>' => escaped.push_str(">"),
'"' => escaped.push_str("""),
'\'' => escaped.push_str("'"),
_ => escaped.push(c),
}
}
Cow::Owned(escaped)
}
pub(crate) fn tag<'a>(
state: &'a crate::html_printer::State<'a>,
tag: &'static str,
attributes: Vec<(String, String)>,
inner: DocBuilder<'a, Arena<'a>, ()>,
) -> DocBuilder<'a, Arena<'a>, ()> {
let mut open_tag = String::with_capacity(tag.len() + 2);
open_tag.push('<');
open_tag.push_str(tag);
for (key, value) in attributes {
open_tag.push(' ');
open_tag.push_str(&key);
open_tag.push_str("=\"");
open_tag.push_str(&escape(&value));
open_tag.push('"');
}
open_tag.push('>');
let mut close_tag = String::with_capacity(tag.len() + 3);
close_tag.push_str("</");
close_tag.push_str(tag);
close_tag.push('>');
state
.arena
.text(open_tag)
.append(inner)
.append(state.arena.text(close_tag))
}