lexa_framework/view/markup/
node.rs1use core::fmt;
12use std::collections::HashMap;
13use std::path;
14use std::sync::Mutex;
15
16mod comment;
17mod doctype;
18mod element;
19mod fragment;
20mod text;
21
22lazy_static::lazy_static! {
27 pub static ref MEMOIZE_FILE: Mutex<
28 HashMap<path::PathBuf, String>
29 > = Mutex::new(HashMap::default());
30}
31
32pub enum Node {
37 Comment(comment::CommentNode),
39 Doctype(doctype::DoctypeNode),
41 Fragment(fragment::FragmentNode),
43 Element(element::ElementNode),
45 Text(text::TextNode),
47 Json(text::JsonTextNode),
49 UnsafeHtml(text::DangerousTextNode),
51}
52
53impl Node {
59 pub fn create_comment(comment: impl ToString) -> Self {
60 Self::Comment(comment::CommentNode {
61 content: comment.to_string(),
62 })
63 }
64}
65
66impl Node {
68 pub fn create_doctype(public_identifier: impl ToString) -> Self {
69 Self::Doctype(doctype::DoctypeNode {
70 public_identifier: public_identifier.to_string(),
71 })
72 }
73}
74
75impl Node {
77 pub fn create_fragment(children: Vec<Node>) -> Self {
78 Self::Fragment(fragment::FragmentNode { children })
79 }
80}
81
82impl Node {
84 pub fn create_element(
85 tag_name: impl ToString,
86 attributes: Vec<(String, Option<String>)>,
87 children: Vec<Node>,
88 ) -> Self {
89 let children = if children.is_empty() {
90 None
91 } else {
92 Some(children)
93 };
94 Self::Element(element::ElementNode {
95 tag_name: tag_name.to_string(),
96 attributes,
97 children,
98 })
99 }
100
101 pub fn create_void_element(
102 tag_name: impl ToString,
103 attributes: Vec<(String, Option<String>)>,
104 ) -> Self {
105 Self::Element(element::ElementNode {
106 tag_name: tag_name.to_string(),
107 attributes,
108 children: None,
109 })
110 }
111}
112
113impl Node {
115 pub fn create_text(text: impl ToString) -> Self {
116 Self::Text(text::TextNode {
117 text: text.to_string(),
118 })
119 }
120
121 pub fn create_json(text: impl ToString) -> Self {
122 Self::Json(text::JsonTextNode {
123 text: text.to_string(),
124 })
125 }
126}
127
128impl Node {
130 pub fn create_unsafe_html(raw_text: impl ToString) -> Self {
131 Self::UnsafeHtml(text::DangerousTextNode {
132 raw_text: raw_text.to_string(),
133 })
134 }
135
136 pub fn create_unsafe_html_from_file(file: impl AsRef<path::Path>) -> Self {
137 let mut memoize = MEMOIZE_FILE.lock().expect("cache guard");
138
139 if let Some(content_of_file) = memoize.get(file.as_ref()) {
140 return Self::UnsafeHtml(text::DangerousTextNode {
141 raw_text: content_of_file.to_owned(),
142 });
143 }
144
145 let raw_text = std::fs::read_to_string(&file).unwrap_or_else(|_| {
146 panic!("le fichier « {} » n'existe pas.", file.as_ref().display())
147 });
148 memoize.insert(file.as_ref().to_owned(), raw_text.clone());
149 Self::UnsafeHtml(text::DangerousTextNode { raw_text })
150 }
151}
152
153fn with_children(
158 f: &mut fmt::Formatter<'_>,
159 children: &[Node],
160 is_fragment: bool,
161) -> fmt::Result {
162 if f.alternate() {
163 let mut children = children.iter();
164
165 if is_fragment {
166 if let Some(first_child) = children.next() {
167 write!(f, "{first_child:#}")?;
168
169 for child in children {
170 write!(f, "\n{child:#}")?;
171 }
172 }
173 } else {
174 for child in children.map(|child| format!("{child:#}")) {
175 for line in child.lines() {
176 write!(f, "\n\t{line}")?;
177 }
178 }
179 writeln!(f)?;
180 }
181 } else {
182 use std::fmt::Display;
183
184 for child in children {
185 child.fmt(f)?;
186 }
187 }
188
189 Ok(())
190}
191
192impl Default for Node {
197 fn default() -> Self {
198 Self::create_fragment(vec![])
199 }
200}
201
202impl fmt::Display for Node {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match &self {
205 | Self::Comment(comment) => comment.fmt(f),
206 | Self::Doctype(doctype) => doctype.fmt(f),
207 | Self::Fragment(fragment) => fragment.fmt(f),
208 | Self::Element(element) => element.fmt(f),
209 | Self::Text(text) => text.fmt(f),
210 | Self::Json(text) => text.fmt(f),
211 | Self::UnsafeHtml(danger) => danger.fmt(f),
212 }
213 }
214}
215
216impl<It, F> From<It> for Node
217where
218 It: IntoIterator<Item = F>,
219 F: Into<Self>,
220{
221 fn from(it: It) -> Self {
222 Self::Fragment(it.into())
223 }
224}
225
226impl From<comment::CommentNode> for Node {
227 fn from(comment_node: comment::CommentNode) -> Self {
228 Self::Comment(comment_node)
229 }
230}
231
232impl From<doctype::DoctypeNode> for Node {
233 fn from(doctype_node: doctype::DoctypeNode) -> Self {
234 Self::Doctype(doctype_node)
235 }
236}
237
238impl From<fragment::FragmentNode> for Node {
239 fn from(fragment_node: fragment::FragmentNode) -> Self {
240 Self::Fragment(fragment_node)
241 }
242}
243
244impl From<element::ElementNode> for Node {
245 fn from(element_node: element::ElementNode) -> Self {
246 Self::Element(element_node)
247 }
248}
249
250impl From<text::TextNode> for Node {
251 fn from(text_node: text::TextNode) -> Self {
252 Self::Text(text_node)
253 }
254}