use std::fmt::Write;
use crate::{Moo, parser::MarkupType, prelude::*};
pub const VOID_ELEMENTS: [&str; 14] = [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr",
];
pub struct HtmlRenderer;
impl Renderer<String> for HtmlRenderer {
fn render(ast: TeaCatDom) -> String {
format!("<!DOCTYPE html><html>{}</html>", render_ast(ast))
}
}
fn render_ast(ast: TeaCatDom) -> String {
let mut rendered = String::new();
for node in ast.0 {
rendered.push_str(&match node {
DomNode::Tag(tag) => render_tag(tag),
DomNode::ContentBlock(ast) => render_ast(ast),
DomNode::Word(word) => render_word(&word),
DomNode::Space => " ".into(),
DomNode::Markup { of, inner } => render_markup(of, inner),
});
}
rendered
}
fn render_markup(of: MarkupType, inner: TeaCatDom) -> String {
let rendered = render_ast(inner);
let tag_name = match of {
MarkupType::Bold => "b",
MarkupType::Italic => "i",
MarkupType::Strikethrough => "s",
MarkupType::Underline => "u",
MarkupType::Header(n) => &format!("h{n}"),
};
format!("<{tag_name}>{rendered}</{tag_name}>")
}
fn render_tag(tag: DomTag) -> String {
if VOID_ELEMENTS.contains(&&*tag.name) {
format!("<{}{} />", tag.name, render_attrs(tag.attrs))
} else {
format!(
"<{}{}>{}</{}>",
tag.name,
render_attrs(tag.attrs),
render_ast(tag.content),
tag.name
)
}
}
fn render_word(text: &Moo) -> String {
html_escape::encode_safe(text).into()
}
fn render_attrs(attrs: Attributes) -> String {
let mut rendered = String::new();
for (key, val) in attrs.0 {
write!(&mut rendered, " {key}=\"{val}\"").expect("Writing should be successful!");
}
rendered
}