1pub(crate) mod template;
2
3use std::collections::HashMap;
4use std::{fmt, hash};
5
6use template::Template;
7
8pub type Children<'a> = Vec<Box<dyn Template + 'a>>;
12
13pub type Attributes<'a> = HashMap<&'a str, &'a str>;
18
19#[derive(Debug)]
34pub struct Element<'a> {
35 tagname: String,
36 children: Children<'a>,
37 attrs: Attributes<'a>,
38}
39
40impl<'a> Element<'a> {
41 pub fn new(tagname: impl ToString) -> Self {
43 Self {
44 tagname: tagname.to_string(),
45 children: Vec::new(),
46 attrs: HashMap::new(),
47 }
48 }
49
50 pub fn with<T: Template + 'a, const N: usize>(tagname: impl ToString, content: [T; N]) -> Self {
54 Self::new(tagname).children(content)
55 }
56
57 pub fn get_tag_name(&self) -> &str {
59 self.tagname.as_str()
60 }
61
62 pub fn get_children(&self) -> &Children {
66 &self.children
67 }
68
69 pub fn get_attrs(&self) -> &Attributes {
71 &self.attrs
72 }
73
74 pub fn add_child<T: Template + 'a>(mut self, template: T) -> Self {
76 self.children.push(Box::new(template));
77
78 self
79 }
80
81 pub fn add_attr(mut self, key: &'a str, value: &'a str) -> Self {
84 self.attrs.insert(key, value);
85
86 self
87 }
88
89 pub fn children_boxed(mut self, ch: Children<'a>) -> Self {
92 self.children = ch;
93
94 self
95 }
96
97 pub fn children<T: Template + 'a, const N: usize>(mut self, ch: [T; N]) -> Self {
99 self.children = ch
100 .into_iter()
101 .map(|c| -> Box<dyn Template> { Box::new(c) })
102 .collect::<Children<'a>>();
103
104 self
105 }
106
107 pub fn attributes(mut self, attrs: Attributes<'a>) -> Self {
109 self.attrs = attrs;
110
111 self
112 }
113}
114
115impl<'a> fmt::Display for Element<'a> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 let children = self
118 .get_children()
119 .iter()
120 .map(|templ| templ.to_string())
121 .collect::<String>();
122
123 let attrs = self
124 .get_attrs()
125 .iter()
126 .map(|(k, v)| format!("{k}=\"{v}\""))
127 .collect::<Vec<_>>()
128 .join(" ");
129
130 let tag = format!(
131 "<{tag} {attrs}>{child}</{tag}>",
132 tag = self.get_tag_name(),
133 child = children,
134 attrs = attrs,
135 )
136 .replace(" >", ">");
137
138 write!(f, "{}", tag)
139 }
140}
141
142impl<'a> Eq for Element<'a> {}
143impl<'a> PartialEq for Element<'a> {
144 fn eq(&self, other: &Self) -> bool {
145 self.tagname == other.get_tag_name() && self.attrs == *other.get_attrs()
146 }
147}
148
149impl<'a> Ord for Element<'a> {
150 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
151 self.to_string().cmp(&other.to_string())
152 }
153}
154
155impl<'a> PartialOrd for Element<'a> {
156 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
157 self.to_string().partial_cmp(&other.to_string())
158 }
159}
160
161impl<'a> Default for Element<'a> {
162 fn default() -> Self {
163 Self::new("")
164 }
165}
166
167impl<'a> hash::Hash for Element<'a> {
168 fn hash<H: hash::Hasher>(&self, state: &mut H) {
169 let stringified = self.to_string();
170 let bytes = stringified.as_bytes();
171
172 state.write(bytes);
173 state.finish();
174 }
175}