use crate::{attributes, node::*, text::Text};
use attributes::{Attribute, Value};
use std::collections::HashMap;
impl<'a> From<Node<'a>> for String {
fn from(value: Node<'a>) -> Self {
match value {
Node::Text(t) => text_to_string(t),
Node::Comment(c) => comment_to_string(c),
Node::Element(e) => element(e),
Node::Void(v) => void_element(v),
}
}
}
fn text_to_string(value: Text) -> String {
value.into()
}
fn comment_to_string(value: Comment) -> String {
let text: String = value.into();
format!("<!--{}-->", text)
}
fn attributes_to_string<'a>(
value: HashMap<attributes::Attribute<'a>, Option<attributes::Value>>,
) -> Option<String> {
let seperator = " ";
let joiner = "=";
let property = |arg: (&Attribute, &Option<Value>)| -> String {
let (key, value) = arg;
match value {
None => format!("{}", key),
Some(value) => format!("{}{}\"{}\"", key, joiner, value),
}
};
match value.len() {
0 => None,
_ => value
.iter()
.map(property)
.collect::<Vec<String>>()
.join(seperator)
.into(),
}
}
fn void_element(value: Element<()>) -> String {
let tag: &str = value.name.into();
let attributes = attributes_to_string(value.attributes);
match attributes {
None => format!("<{}/>", tag),
Some(attributes) => format!("<{} {} />", tag, attributes),
}
}
fn element(value: Element<Vec<Node>>) -> String {
let tag: &str = value.name.into();
let attributes = attributes_to_string(value.attributes);
let string_child = |node: &Node| -> String { node.clone().into() };
let child_sep = "";
let children = value
.children
.iter()
.map(string_child)
.collect::<Vec<String>>()
.join(child_sep);
match attributes {
None => format!("<{}>{}</{}>", tag, children, tag),
Some(attr) => format!("<{} {}>{}</{}>", tag, attr, children, tag),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tag::Tag;
use attributes::{Attribute, Value};
#[test]
fn render_text() {
let text = Node::text("Hello");
let rendered: String = text.into();
let expected = "Hello";
assert_eq!(rendered, expected);
}
#[test]
fn render_comment() {
let text = Node::comment("Hello");
let rendered: String = text.into();
let expected = "<!--Hello-->";
assert_eq!(rendered, expected);
}
#[test]
fn render_element_void() {
let element: Node = Element::<()>::create(Tag::BR).into();
let expected = "<br/>";
let render: String = element.into();
assert_eq!(render, expected);
}
#[test]
fn render_element_void_with_attributes() {
let mut element = Element::<()>::create(Tag::BR);
element.set_attribute(Attribute::CLASS, Value::create("test").unwrap());
let expected = r#"<br class="test" />"#;
let node: Node = element.into();
let render: String = node.into();
assert_eq!(render, expected);
}
#[test]
fn render_element_open() {
let mut element = Element::<Vec<Node>>::create(Tag::A);
let nested: Node = Text::create("Link").into();
element.push(nested);
let node: Node = element.into();
let rendered: String = node.into();
let expected = "<a>Link</a>";
assert_eq!(rendered, expected);
}
#[test]
fn render_element_multi_nested() {
let div = Element::<Vec<Node>>::create(Tag::DIV);
let mut div2 = div.clone();
let mut div3 = div.clone();
div2.push(div);
div3.push(div2);
let node: Node = div3.into();
let rendered: String = node.into();
let expected = "<div><div><div></div></div></div>";
assert_eq!(rendered, expected);
}
#[test]
fn render_element_two_children() {
let div = Element::<Vec<Node>>::create(Tag::DIV);
let div2 = div.clone();
let mut div3 = div.clone();
div3.push(div);
div3.push(div2);
let node: Node = div3.into();
let rendered: String = node.into();
let expected = "<div><div></div><div></div></div>";
assert_eq!(rendered, expected);
}
#[test]
fn render_element_open_but_empty() {
let element = Element::<Vec<Node>>::create(Tag::A);
let node: Node = element.into();
let rendered: String = node.into();
let expected = "<a></a>";
assert_eq!(rendered, expected);
}
#[test]
fn render_element_open_with_attributes() {
let mut element = Element::<Vec<Node>>::create(Tag::A);
element.set_attribute(Attribute::CLASS, Value::create("test").unwrap());
let nested: Node = Text::create("Link").into();
element.push(nested);
let node: Node = element.into();
let rendered: String = node.into();
let expected = r#"<a class="test">Link</a>"#;
assert_eq!(rendered, expected);
}
}