1mod a;
2mod abbr;
3mod address;
4mod area;
5mod article;
6mod aside;
7mod audio;
8mod b;
9mod base;
10mod bdi;
11mod bdo;
12mod blockquote;
13mod body;
14mod br;
15mod button;
16mod canvas;
17mod caption;
18mod cite;
19mod code;
20mod col;
21mod colgroup;
22mod data;
23mod datalist;
24mod dd;
25mod del;
26mod details;
27mod dfn;
28mod dialog;
29mod div;
30mod dl;
31mod dt;
32mod em;
33mod embed;
34mod fieldset;
35mod figcaption;
36mod figure;
37mod footer;
38mod form;
39mod h1;
40mod h2;
41mod h3;
42mod h4;
43mod h5;
44mod h6;
45mod head;
46mod header;
47mod hgroup;
48mod hr;
49mod html;
50mod i;
51mod iframe;
52mod img;
53mod innerhtml;
54mod input;
55mod ins;
56mod kbd;
57mod label;
58mod legend;
59mod li;
60mod link;
61mod main;
62mod map;
63mod mark;
64mod marquee;
65mod menu;
66mod meta;
67mod meter;
68mod nav;
69mod noscript;
70mod object;
71mod ol;
72mod optgroup;
73mod option;
74mod outerhtml;
75mod output;
76mod p;
77mod picture;
78mod pre;
79mod progress;
80mod q;
81mod rp;
82mod rt;
83mod ruby;
84mod s;
85mod samp;
86mod script;
87mod search;
88mod section;
89mod select;
90mod slot;
91mod small;
92mod source;
93mod span;
94mod strong;
95mod style;
96mod sub;
97mod summary;
98mod table;
99mod tbody;
100mod td;
101mod template;
102mod text;
103mod textarea;
104mod tfoot;
105mod th;
106mod thead;
107mod time;
108mod title;
109mod tr;
110mod track;
111mod u;
112mod ul;
113mod var;
114mod video;
115mod wbr;
116
117use std::{
118 borrow::Cow,
119 fmt::{Debug, Display},
120};
121
122pub use self::{
123 a::A, abbr::Abbr, address::Address, area::Area, article::Article, aside::Aside, audio::Audio,
124 b::B, base::Base, bdi::Bdi, bdo::Bdo, blockquote::Blockquote, body::Body, br::Br,
125 button::Button, canvas::Canvas, caption::Caption, cite::Cite, code::Code, col::Col,
126 colgroup::Colgroup, data::Data, datalist::Datalist, dd::Dd, del::Del, details::Details,
127 dfn::Dfn, dialog::Dialog, div::Div, dl::Dl, dt::Dt, em::Em, embed::Embed, fieldset::Fieldset,
128 figcaption::Figcaption, figure::Figure, footer::Footer, form::Form, h1::H1, h2::H2, h3::H3,
129 h4::H4, h5::H5, h6::H6, head::Head, header::Header, hgroup::Hgroup, hr::Hr, html::Html, i::I,
130 iframe::Iframe, img::Img, innerhtml::Innerhtml, input::Input, ins::Ins, kbd::Kbd, label::Label,
131 legend::Legend, li::Li, link::Link, main::Main, map::Map, mark::Mark, marquee::Marquee,
132 menu::Menu, meta::Meta, meter::Meter, nav::Nav, noscript::Noscript, object::Object, ol::Ol,
133 optgroup::Optgroup, option::OptionElement, outerhtml::Outerhtml, output::Output, p::P,
134 picture::Picture, pre::Pre, progress::Progress, q::Q, rp::Rp, rt::Rt, ruby::Ruby, s::S,
135 samp::Samp, script::Script, search::Search, section::Section, select::Select, slot::Slot,
136 small::Small, source::Source, span::Span, strong::Strong, style::Style, sub::Sub,
137 summary::Summary, table::Table, tbody::Tbody, td::Td, template::Template, text::TextContent,
138 textarea::Textarea, tfoot::Tfoot, th::Th, thead::Thead, time::Time, title::Title, tr::Tr,
139 track::Track, u::U, ul::Ul, var::Var, video::Video, wbr::Wbr,
140};
141
142use crate::{tags::Tag, OUTPUT_IDENTATION};
143
144pub trait ElementName: Debug {
145 fn name(&self) -> &'static str;
146}
147
148pub trait ElementBuilder<'a> {
149 fn builder() -> HtmlElement<'a>;
150}
151
152#[derive(Debug, PartialEq, Eq)]
153pub enum HtmlElementChildren<'a> {
154 TextContent(Cow<'a, str>),
155 Children(Vec<HtmlElement<'a>>),
156}
157
158impl Display for HtmlElementChildren<'_> {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 let output = match self {
161 HtmlElementChildren::TextContent(text) => text.to_owned(),
162 HtmlElementChildren::Children(html_elements) => {
163 let mut output = "".to_owned();
164 for elem in html_elements {
165 output.push_str(elem.to_string().as_str())
166 }
167 output.into()
168 }
169 };
170
171 write!(f, "{output}")
172 }
173}
174
175#[derive(Debug)]
176pub struct HtmlElement<'a> {
177 pub tag: Tag,
178 depth: usize,
179 pub children: Option<HtmlElementChildren<'a>>,
180}
181
182impl Display for HtmlElement<'_> {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 let mut output = "".to_owned();
185 let iden = " ".repeat(OUTPUT_IDENTATION * self.depth);
186
187 let tagname_and_attrs = format!("{}", self.tag);
188 let tag_name = &self.tag.element.name();
189
190 match &self.children {
191 Some(children) => {
192 if let HtmlElementChildren::TextContent(s) = children {
193 let text_iden = " ".repeat(OUTPUT_IDENTATION * (self.depth + 1));
194 output.push_str(format!("\n{text_iden}{s}").as_str())
195 } else {
196 output.push_str(
197 format!("\n{iden}<{tagname_and_attrs}>{children}\n{iden}</{tag_name}>")
198 .as_str(),
199 )
200 }
201 }
202 None => output.push_str(format!("\n{iden}<{tagname_and_attrs}></{tag_name}>").as_str()),
203 };
204
205 write!(f, "{output}")
206 }
207}
208impl PartialEq for HtmlElement<'_> {
209 fn eq(&self, other: &Self) -> bool {
210 self.tag == other.tag && self.depth == other.depth && self.children == other.children
211 }
212}
213
214impl Eq for HtmlElement<'_> {}
215impl<'a> HtmlElement<'a> {
216 pub fn builder(tag: Tag) -> HtmlElement<'a> {
217 HtmlElement {
218 tag,
219 depth: 2,
221 children: Default::default(),
222 }
223 }
224 pub fn inner_text<S: AsRef<str>>(mut self, text: S) -> HtmlElement<'a> {
226 self.children = Some(HtmlElementChildren::TextContent(
227 text.as_ref().to_owned().into(),
228 ));
229
230 HtmlElement {
231 tag: self.tag,
232 depth: self.depth,
233 children: self.children,
234 }
235 }
236 pub fn attr<K: AsRef<str>, V: AsRef<str>>(mut self, key: K, value: V) -> HtmlElement<'a> {
237 self.tag.set_attr(key, value);
238
239 HtmlElement {
240 tag: self.tag,
241 depth: self.depth,
242 children: self.children,
243 }
244 }
245 pub fn append_child(self, mut new_element: HtmlElement<'a>) -> HtmlElement<'a> {
246 new_element.depth = self.depth + 1;
247 if let Some(children) = self.children {
249 let new_children = match children {
250 HtmlElementChildren::TextContent(text) => {
251 let migrated = TextContent::text(text);
252 HtmlElementChildren::Children(vec![migrated, new_element])
253 }
254 HtmlElementChildren::Children(mut html_elements) => {
255 html_elements.push(new_element);
256 HtmlElementChildren::Children(html_elements)
257 }
258 };
259 HtmlElement {
260 tag: self.tag,
261 depth: self.depth,
262 children: Some(new_children),
263 }
264 } else {
265 HtmlElement {
266 tag: self.tag,
267 depth: self.depth,
268 children: Some(HtmlElementChildren::Children(vec![new_element])),
269 }
270 }
271 }
272
273 pub fn depth(&self) -> usize {
274 self.depth
275 }
276
277 pub fn set_depth(&mut self, depth: usize) {
278 self.depth = depth;
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use text::TextContent;
285
286 use super::*;
287 use crate::elements::{div::Div, p::P};
288 #[test]
289 fn ok_on_build_div_with_paragraph() {
290 let div = Div::builder().attr("class", "light-theme").append_child(
291 P::builder()
292 .attr("class", "light-theme")
293 .append_child(TextContent::text("It Works!")),
294 );
295
296 println!("{div}");
297 }
298}