html_types/
render.rs

1///! Converts the node types into a string
2///! This will correctly call child elements
3///! Thus allowing us to format an arbitrary
4///! Tree of nodes into html
5///!
6///! NOTE: Currently functional, but not pretty-printed
7use crate::{attributes, node::*, text::Text};
8use attributes::{Attribute, Value};
9use std::collections::HashMap;
10
11impl<'a> From<Node<'a>> for String {
12    fn from(value: Node<'a>) -> Self {
13        match value {
14            Node::Text(t) => text_to_string(t),
15            Node::Comment(c) => comment_to_string(c),
16            Node::Element(e) => element(e),
17            Node::Void(v) => void_element(v),
18        }
19    }
20}
21
22fn text_to_string(value: Text) -> String {
23    value.into()
24}
25
26fn comment_to_string(value: Comment) -> String {
27    let text: String = value.into();
28    format!("<!--{}-->", text)
29}
30
31fn attributes_to_string<'a>(
32    value: HashMap<attributes::Attribute<'a>, Option<attributes::Value>>,
33) -> Option<String> {
34    let seperator = " ";
35    let joiner = "=";
36    let property = |arg: (&Attribute, &Option<Value>)| -> String {
37        let (key, value) = arg;
38        match value {
39            None => format!("{}", key),
40            Some(value) => format!("{}{}\"{}\"", key, joiner, value),
41        }
42    };
43
44    match value.len() {
45        0 => None,
46        _ => value
47            .iter()
48            .map(property)
49            .collect::<Vec<String>>()
50            .join(seperator)
51            .into(),
52    }
53}
54
55fn void_element(value: Element<()>) -> String {
56    let tag: &str = value.name.into();
57    let attributes = attributes_to_string(value.attributes);
58    match attributes {
59        None => format!("<{}/>", tag),
60        Some(attributes) => format!("<{} {} />", tag, attributes),
61    }
62}
63
64fn element(value: Element<Vec<Node>>) -> String {
65    let tag: &str = value.name.into();
66    let attributes = attributes_to_string(value.attributes);
67    let string_child = |node: &Node| -> String { node.clone().into() };
68    let child_sep = "";
69    let children = value
70        .children
71        .iter()
72        .map(string_child)
73        .collect::<Vec<String>>()
74        .join(child_sep);
75    match attributes {
76        None => format!("<{}>{}</{}>", tag, children, tag),
77        Some(attr) => format!("<{} {}>{}</{}>", tag, attr, children, tag),
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::tag::Tag;
85    use attributes::{Attribute, Value};
86    #[test]
87    fn render_text() {
88        let text = Node::text("Hello");
89        let rendered: String = text.into();
90        let expected = "Hello";
91        assert_eq!(rendered, expected);
92    }
93
94    #[test]
95    fn render_comment() {
96        let text = Node::comment("Hello");
97        let rendered: String = text.into();
98        let expected = "<!--Hello-->";
99        assert_eq!(rendered, expected);
100    }
101
102    #[test]
103    fn render_element_void() {
104        let element: Node = Element::<()>::create(Tag::BR).into();
105        let expected = "<br/>";
106        let render: String = element.into();
107        assert_eq!(render, expected);
108    }
109
110    #[test]
111    fn render_element_void_with_attributes() {
112        let mut element = Element::<()>::create(Tag::BR);
113        element.set_attribute(Attribute::CLASS, Value::create("test").unwrap());
114        let expected = r#"<br class="test" />"#;
115        let node: Node = element.into();
116        let render: String = node.into();
117        assert_eq!(render, expected);
118    }
119
120    #[test]
121    fn render_element_open() {
122        let mut element = Element::<Vec<Node>>::create(Tag::A);
123        let nested: Node = Text::create("Link").into();
124        element.push(nested);
125        let node: Node = element.into();
126        let rendered: String = node.into();
127        let expected = "<a>Link</a>";
128        assert_eq!(rendered, expected);
129    }
130
131    #[test]
132    fn render_element_multi_nested() {
133        let div = Element::<Vec<Node>>::create(Tag::DIV);
134        let mut div2 = div.clone();
135        let mut div3 = div.clone();
136        div2.push(div);
137        div3.push(div2);
138
139        let node: Node = div3.into();
140        let rendered: String = node.into();
141        let expected = "<div><div><div></div></div></div>";
142        assert_eq!(rendered, expected);
143    }
144
145    #[test]
146    fn render_element_two_children() {
147        let div = Element::<Vec<Node>>::create(Tag::DIV);
148        let div2 = div.clone();
149        let mut div3 = div.clone();
150        div3.push(div);
151        div3.push(div2);
152
153        let node: Node = div3.into();
154        let rendered: String = node.into();
155        let expected = "<div><div></div><div></div></div>";
156        assert_eq!(rendered, expected);
157    }
158
159    #[test]
160    fn render_element_open_but_empty() {
161        let element = Element::<Vec<Node>>::create(Tag::A);
162        let node: Node = element.into();
163        let rendered: String = node.into();
164        let expected = "<a></a>";
165        assert_eq!(rendered, expected);
166    }
167
168    #[test]
169    fn render_element_open_with_attributes() {
170        let mut element = Element::<Vec<Node>>::create(Tag::A);
171        element.set_attribute(Attribute::CLASS, Value::create("test").unwrap());
172        let nested: Node = Text::create("Link").into();
173        element.push(nested);
174        let node: Node = element.into();
175        let rendered: String = node.into();
176        let expected = r#"<a class="test">Link</a>"#;
177        assert_eq!(rendered, expected);
178    }
179}