use pretty::{Arena, DocAllocator, DocBuilder};

pub(super) fn escape(value: &str) -> String {
    let mut escaped = String::new();
    for c in value.chars() {
        match c {
            '&' => escaped.push_str("&amp;"),
            '<' => escaped.push_str("&lt;"),
            '>' => escaped.push_str("&gt;"),
            '"' => escaped.push_str("&quot;"),
            '\'' => escaped.push_str("&apos;"),
            _ => escaped.push(c),
        }
    }
    escaped
}

pub(super) trait ArenaExt<'a> {
    fn tag(
        &'a self,
        tag: &'static str,
        attributes: Vec<(String, String)>,
        inner: DocBuilder<'a, Arena<'a>, ()>,
    ) -> DocBuilder<'a, Arena<'a>, ()>;
}

impl<'a> ArenaExt<'a> for Arena<'a> {
    fn tag(
        &'a self,
        tag: &'static str,
        attributes: Vec<(String, String)>,
        inner: DocBuilder<'a, Self, ()>,
    ) -> DocBuilder<'a, Self, ()> {
        let mut attrs = self.nil();
        for (key, value) in attributes {
            let attr = if value.is_empty() {
                self.text(" ").append(self.text(key))
            } else {
                self.text(" ")
                    .append(self.text(key))
                    .append(self.text("=\""))
                    .append(self.text(escape(&value)))
                    .append(self.text("\""))
            };

            attrs = attrs.append(attr);
        }
        let open_tag = self
            .text("<")
            .append(self.text(tag))
            .append(attrs)
            .append(self.text(">"));
        let close_tag = self
            .text("</")
            .append(self.text(tag))
            .append(self.text(">"));
        open_tag.append(inner).append(close_tag)
    }
}
