1enum AttrType {
4 KV(String, String),
5 Bool(String),
6 Data(String, String),
7 BoolData(String)
8}
9
10pub struct Element {
11 tag: String,
12 classes: Vec<String>,
13 alst: Vec<AttrType>
14}
15
16impl Element {
17 #[must_use]
18 #[allow(clippy::needless_pass_by_value)]
19 pub fn new(tag: impl ToString) -> Self {
20 Self {
21 tag: tag.to_string(),
22 classes: Vec::new(),
23 alst: Vec::new()
24 }
25 }
26
27 #[must_use]
28 #[allow(clippy::needless_pass_by_value)]
29 pub fn class(mut self, cls: impl ToString) -> Self {
30 self.class_r(cls);
31 self
32 }
33
34 #[allow(clippy::needless_pass_by_value)]
35 pub fn class_r(&mut self, cls: impl ToString) -> &mut Self {
36 self.classes.push(cls.to_string());
37 self
38 }
39
40 #[must_use]
41 #[allow(clippy::needless_pass_by_value)]
42 pub fn flag(mut self, key: impl ToString) -> Self {
43 self.flag_r(key);
44 self
45 }
46
47 #[allow(clippy::needless_pass_by_value)]
48 pub fn flag_r(&mut self, key: impl ToString) -> &mut Self {
49 self.alst.push(AttrType::Bool(key.to_string()));
50 self
51 }
52
53 #[must_use]
54 pub fn attr(mut self, key: impl ToString, value: impl AsRef<str>) -> Self {
55 self.attr_r(key, value);
56 self
57 }
58
59 #[allow(clippy::needless_pass_by_value)]
60 pub fn attr_r(
61 &mut self,
62 key: impl ToString,
63 value: impl AsRef<str>
64 ) -> &mut Self {
65 let key = key.to_string();
66 debug_assert!(
67 key != "class",
68 "Use the dedicated .class() method to add classes to elements"
69 );
70 self.alst.push(AttrType::KV(
71 key,
72 html_escape::encode_double_quoted_attribute(value.as_ref()).to_string()
73 ));
74 self
75 }
76
77 #[must_use]
78 #[allow(clippy::needless_pass_by_value)]
79 pub fn data_attr(
80 mut self,
81 key: impl ToString,
82 value: impl AsRef<str>
83 ) -> Self {
84 self.data_attr_r(key, value);
85 self
86 }
87
88 #[allow(clippy::needless_pass_by_value)]
89 pub fn data_attr_r(
90 &mut self,
91 key: impl ToString,
92 value: impl AsRef<str>
93 ) -> &mut Self {
94 let key = key.to_string();
95 self.alst.push(AttrType::Data(
96 key,
97 html_escape::encode_double_quoted_attribute(value.as_ref()).to_string()
98 ));
99 self
100 }
101
102 #[must_use]
103 #[allow(clippy::needless_pass_by_value)]
104 pub fn data_flag(mut self, key: impl ToString) -> Self {
105 self.data_flag_r(key);
106 self
107 }
108
109 #[allow(clippy::needless_pass_by_value)]
110 pub fn data_flag_r(&mut self, key: impl ToString) -> &mut Self {
111 self.alst.push(AttrType::BoolData(key.to_string()));
112 self
113 }
114}
115
116impl Element {
117 #[must_use]
118 #[allow(clippy::needless_pass_by_value)]
119 pub fn raw_attr(mut self, key: impl ToString, value: impl ToString) -> Self {
120 self.raw_attr_r(key, value);
121 self
122 }
123
124 #[allow(clippy::needless_pass_by_value)]
125 pub fn raw_attr_r(
126 &mut self,
127 key: impl ToString,
128 value: impl ToString
129 ) -> &mut Self {
130 let key = key.to_string();
131 debug_assert!(
132 key != "class",
133 "Use the dedicated .class() method to add classes to elements"
134 );
135 self.alst.push(AttrType::KV(key, value.to_string()));
136 self
137 }
138}
139
140impl Element {
141 #[must_use]
150 pub fn map_if<F>(self, flag: bool, f: F) -> Self
151 where
152 F: FnOnce(Self) -> Self
153 {
154 if flag {
155 f(self)
156 } else {
157 self
158 }
159 }
160
161 #[must_use]
162 pub fn map_opt<T, F>(self, opt: Option<T>, f: F) -> Self
163 where
164 F: FnOnce(Self, T) -> Self
165 {
166 if let Some(o) = opt {
167 f(self, o)
168 } else {
169 self
170 }
171 }
172
173 pub fn mod_if<F>(&mut self, flag: bool, f: F) -> &mut Self
185 where
186 F: FnOnce(&mut Self)
187 {
188 if flag {
189 f(self);
190 }
191 self
192 }
193
194 pub fn mod_opt<T, F>(&mut self, opt: Option<T>, f: F) -> &mut Self
195 where
196 F: FnOnce(&mut Self, T)
197 {
198 if let Some(o) = opt {
199 f(self, o);
200 }
201 self
202 }
203}
204
205impl Element {
206 fn gen_attr_list(&self) -> Option<Vec<String>> {
208 if self.alst.is_empty() && self.classes.is_empty() {
209 None
210 } else {
211 let mut ret = Vec::new();
212
213 if !self.classes.is_empty() {
214 ret.push(format!(r#"class="{}""#, self.classes.join(" ")));
215 }
216
217 let it = self.alst.iter().map(|a| match a {
218 AttrType::KV(k, v) => {
219 format!(r#"{k}="{v}""#)
220 }
221 AttrType::Bool(a) => a.clone(),
222 AttrType::Data(k, v) => {
223 format!(r#"data-{k}="{v}""#)
224 }
225 AttrType::BoolData(a) => {
226 format!("data-{a}")
227 }
228 });
229
230 ret.extend(it);
231
232 Some(ret)
233 }
234 }
235}
236
237impl Element {
238 pub fn sub<F>(self, bldr: &mut sidoc::Builder, f: F)
250 where
251 F: FnOnce(&mut sidoc::Builder)
252 {
253 if let Some(lst) = self.gen_attr_list() {
254 bldr.scope(
255 format!("<{} {}>", self.tag, lst.join(" ")),
256 Some(format!("</{}>", self.tag))
257 );
258 } else {
259 let stag = format!("<{}>", self.tag);
260 let etag = format!("</{}>", self.tag);
261 bldr.scope(stag, Some(etag));
262 }
263
264 f(bldr);
265
266 bldr.exit();
267 }
268}
269
270
271impl Element {
272 #[inline]
278 pub fn add_empty(self, bldr: &mut sidoc::Builder) {
279 let line = if let Some(alst) = self.gen_attr_list() {
280 format!("<{} {}>", self.tag, alst.join(" "))
281 } else {
282 format!("<{}>", self.tag)
283 };
284
285 bldr.line(line);
286 }
287
288 #[inline]
314 pub fn add_content(self, text: &str, bldr: &mut sidoc::Builder) {
315 let line = if let Some(alst) = self.gen_attr_list() {
316 format!(
317 "<{} {}>{}</{}>",
318 self.tag,
319 alst.join(" "),
320 html_escape::encode_text(text),
321 self.tag
322 )
323 } else {
324 format!(
325 "<{}>{}</{}>",
326 self.tag,
327 html_escape::encode_text(text),
328 self.tag
329 )
330 };
331 bldr.line(line);
332 }
333
334 #[inline]
354 pub fn add_raw_content(self, text: &str, bldr: &mut sidoc::Builder) {
355 let line = if let Some(alst) = self.gen_attr_list() {
356 format!("<{} {}>{}</{}>", self.tag, alst.join(" "), text, self.tag)
357 } else {
358 format!("<{}>{}</{}>", self.tag, text, self.tag)
359 };
360 bldr.line(line);
361 }
362
363 pub fn add_scope(self, bldr: &mut sidoc::Builder) {
364 let line = if let Some(alst) = self.gen_attr_list() {
365 format!("<{} {}>", self.tag, alst.join(" "))
366 } else {
367 format!("<{}>", self.tag)
368 };
369 bldr.scope(line, Some(format!("</{}>", self.tag)));
370 }
371
372 pub fn scope<F>(self, bldr: &mut sidoc::Builder, f: F)
395 where
396 F: FnOnce(&mut sidoc::Builder)
397 {
398 let line = if let Some(alst) = self.gen_attr_list() {
399 format!("<{} {}>", self.tag, alst.join(" "))
400 } else {
401 format!("<{}>", self.tag)
402 };
403 bldr.scope(line, Some(format!("</{}>", self.tag)));
404 f(bldr);
405 bldr.exit();
406 }
407}
408
409