trowel 0.2.0

Tools for writing HTML in Rust
Documentation
use std::fmt::Write;

pub type Attribute = (String, Option<String>);

pub trait HtmlWriter {
    fn write_tag(&mut self, tag: &str, single: bool, attributes: Vec<Attribute>);
    fn write_end_tag(&mut self, tag: &str);
    fn write_string_lit(&mut self, lit: &str);
}

pub trait ToHtml {
    fn to_html(self, writer: &mut dyn HtmlWriter);
}

impl<F: FnOnce(&mut dyn crate::HtmlWriter)> ToHtml for F {
    fn to_html(self, writer: &mut dyn HtmlWriter) {
        self(writer)
    }
}

impl ToHtml for &str {
    fn to_html(self, writer: &mut dyn HtmlWriter) {
        writer.write_string_lit(self);
    }
}

impl ToHtml for String {
    fn to_html(self, writer: &mut dyn HtmlWriter) {
        writer.write_string_lit(self.as_str());
    }
}

impl<T> ToHtml for Vec<T>
where
    T: Fn(&mut dyn HtmlWriter),
{
    fn to_html(self, writer: &mut dyn HtmlWriter) {
        for el in self {
            el(writer)
        }
    }
}

impl<T: ToHtml> ToHtml for Option<T> {
    fn to_html(self, writer: &mut dyn HtmlWriter) {
        if let Some(x) = self {
            x.to_html(writer)
        }
    }
}

pub struct WriteHtml<T: Write> {
    writer: T,
}

impl<T: Write> WriteHtml<T> {
    pub fn new(writer: T) -> Self {
        WriteHtml { writer }
    }
}

impl<T: Write> HtmlWriter for WriteHtml<T> {
    fn write_tag(&mut self, tag: &str, single: bool, attr: Vec<Attribute>) {
        let end = if single { " /" } else { "" };
        let mut attr_str = String::new();
        for (key, value) in attr {
            attr_str.push(' ');
            attr_str.push_str(&key);
            if let Some(value) = value {
                attr_str.push('=');
                attr_str.push('"');
                attr_str.push_str(&value);
                attr_str.push('"');
            }
        }
        write!(self.writer, "<{tag}{attr_str}{end}>").unwrap();
    }

    fn write_end_tag(&mut self, tag: &str) {
        write!(self.writer, "</{tag}>").unwrap();
    }

    fn write_string_lit(&mut self, lit: &str) {
        writeln!(self.writer, "{lit}").unwrap()
    }
}