html_rs/
elements.rs

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            // It exists from HtmlBody(depth=2)
220            depth: 2,
221            children: Default::default(),
222        }
223    }
224    /// ## Attention: Only use in TextContent
225    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        // dbg!(&new_element);
248        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}