1use std::{cell::RefCell, collections::HashMap, rc::Rc};
2
3use crate::ast::Element;
4
5const TAB: &str = " ";
6const LINE: &str = "\r\n";
7
8#[inline(always)]
10fn stringify_attrubutes_hash(attribute_hash: &RefCell<HashMap<String, &str>>) -> String {
11 let mut arr: Vec<(String, &str)> = attribute_hash
12 .borrow_mut()
13 .iter()
14 .map(|(k, v)| ((*k).clone(), *v))
15 .collect();
16 arr.sort_by(|(ak, _), (bk, _)| ak.cmp(bk));
17 arr.iter()
18 .fold("".to_string(), |c, (k, v)| format!("{} {}=\"{}\"", c, k, v))
19}
20
21fn trasverse(
23 ele: Rc<Element>,
24 z: usize,
25 (tab_mark, line_mark): (&'static str, &'static str),
26) -> String {
27 let Element {
28 ele_type,
29 attributes,
30 parent: _,
31 children,
32 } = &*ele;
33 let attrs_str = stringify_attrubutes_hash(attributes);
34 let content = if children.borrow().is_empty() {
35 format!(
36 "{}<{}{}/>{}",
37 tab_mark.repeat(z),
38 ele_type,
39 attrs_str,
40 line_mark
41 )
42 } else {
43 let children_str = children
44 .borrow()
45 .iter()
46 .map(|t| trasverse(t.clone(), z + 1, (tab_mark, line_mark)))
47 .collect::<Vec<String>>()
48 .join("");
49 format!(
50 "{}<{}{}>{}{}{}</{}>{}",
51 tab_mark.repeat(z),
52 ele_type,
53 attrs_str,
54 line_mark,
55 children_str,
56 tab_mark.repeat(z),
57 ele_type,
58 line_mark
59 )
60 };
61 content
62}
63
64pub fn stringify(ele: Rc<Element>) -> String {
93 trasverse(ele, 0, ("", ""))
94}
95
96pub fn stringify_pretty(ele: Rc<Element>) -> String {
125 trasverse(ele, 0, (TAB, LINE))
126}
127
128#[cfg(test)]
129mod tests {
130 use std::{cell::RefCell, collections::HashMap};
131
132 use crate::{
133 stringify::{stringify_attrubutes_hash, trasverse},
134 Element,
135 };
136
137 #[test]
138 fn test_stringify_attrubutes_hash() {
139 let attrs = RefCell::new(HashMap::from([
140 ("cx".to_owned(), "100"),
141 ("cy".to_owned(), "50"),
142 ("r".to_owned(), "40"),
143 ("stroke".to_owned(), "black"),
144 ("stroke-width".to_owned(), "2"),
145 ("fill".to_owned(), "red"),
146 ]));
147 assert_eq!(
148 stringify_attrubutes_hash(&attrs),
149 r#" cx="100" cy="50" fill="red" r="40" stroke="black" stroke-width="2""#
150 );
151 }
152
153 #[test]
154 fn test_trasverse() {
155 let root = Element::new_width_children((
156 "svg",
157 HashMap::from([
158 ("xmlns".to_owned(), "http://www.w3.org/2000/svg"),
159 ("version".to_owned(), "1.1"),
160 ]),
161 vec![Element::new((
162 "circle",
163 HashMap::from([
164 ("cx".to_owned(), "100"),
165 ("cy".to_owned(), "50"),
166 ("r".to_owned(), "40"),
167 ("stroke".to_owned(), "black"),
168 ("stroke-width".to_owned(), "2"),
169 ("fill".to_owned(), "red"),
170 ]),
171 ))],
172 ));
173 assert_eq!(
174 trasverse(root.clone(), 0, ("", "")),
175 r#"<svg version="1.1" xmlns="http://www.w3.org/2000/svg"><circle cx="100" cy="50" fill="red" r="40" stroke="black" stroke-width="2"/></svg>"#
176 );
177
178 assert_eq!(
179 trasverse(root.clone(), 0, (" ", "\r\n")),
180 "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <circle cx=\"100\" cy=\"50\" fill=\"red\" r=\"40\" stroke=\"black\" stroke-width=\"2\"/>\r\n</svg>\r\n"
181 );
182 }
183}