write_html/
tags.rs

1/*!
2Provides functions for creating common tags.
3*/
4
5use crate::{Attributes, Html, Compactability, Sum, AttributeName, AttributeValue, HtmlEnv, Empty};
6
7
8/// Represents a tag.
9///
10/// TODO better docs
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct Tag<'t, A: Attributes, I: Html, const SILENT: bool> { // TODO maybe with another type or an option
13    tag: &'t str,
14    attributes: A,
15    inner_html: I,
16    compactability: Compactability,
17}
18
19impl<'n, A: Attributes, I: Html, const SILENT: bool> Tag<'n, A, I, SILENT> {
20    /// Adds a child to the tag.
21    ///
22    /// # Arguments
23    /// * `child` - The child to add.
24    ///
25    /// # Example
26    /// ```
27    /// use write_html::*;
28    /// use std::fmt::Write;
29    ///
30    /// let mut s = String::new();
31    /// s.write_html(tags::div(Empty, Empty)
32    ///     .child(tags::p(Empty, Empty)
33    ///         .child("Hello, world!".as_html())
34    ///     )
35    /// ).unwrap();
36    /// assert_eq!(s, "<div><p>Hello, world!</p></div>");
37    /// ```
38    pub fn child<C: Html>(
39        self,
40        child: C,
41    ) -> Tag<'n, A, Sum<I, C>, SILENT> {
42        Tag {
43            tag: self.tag,
44            attributes: self.attributes,
45            inner_html: Sum(self.inner_html, child),
46            compactability: self.compactability,
47        }
48    }
49
50    /// Adds multiple attributes to the tag.
51    ///
52    /// # Arguments
53    /// * `attributes` - The attributes to add.
54    ///
55    /// # Example
56    /// ```
57    /// use write_html::*;
58    /// use std::fmt::Write;
59    ///
60    /// let mut s = String::new();
61    /// s.write_html(tags::div(Empty, Empty)
62    ///     .attributes([
63    ///         ("class", "container"),
64    ///         ("id", "main"),
65    ///     ])
66    /// ).unwrap();
67    /// assert_eq!(s, "<div class=\"container\" id=\"main\"></div>");
68    /// ```
69    pub fn attributes<B: Attributes>(
70        self,
71        attributes: B,
72    ) -> Tag<'n, Sum<A, B>, I, SILENT> {
73        Tag {
74            tag: self.tag,
75            attributes: Sum(self.attributes, attributes),
76            inner_html: self.inner_html,
77            compactability: self.compactability,
78        }
79    }
80
81    /// Adds an attribute to the tag.
82    pub fn attr<Name: AttributeName, Value: AttributeValue>(
83        self,
84        name: Name,
85        value: Value,
86    ) -> Tag<'n, Sum<A, [(Name, Value); 1]>, I, SILENT> {
87        Tag {
88            tag: self.tag,
89            attributes: Sum(self.attributes, [(name, value)]),
90            inner_html: self.inner_html,
91            compactability: self.compactability,
92        }
93    }
94}
95
96impl<'n, A: Attributes, I: Html, const SILENT: bool> Html for Tag<'n, A, I, SILENT> {
97    fn write_html(self, env: &mut impl crate::HtmlEnv) -> std::fmt::Result {
98        if SILENT {
99            if !self.inner_html.is_unit() {
100                env.write_html(self.inner_html).map(|_| ())
101            } else {
102                Ok(())
103            }
104        } else {
105            if self.inner_html.is_unit() {
106                env
107                    .open_tag(self.tag, self.compactability)?
108                    .with_attributes(self.attributes)
109                    .map(|_| ())
110            } else {
111                env
112                    .open_tag(self.tag, self.compactability)?
113                    .with_attributes(self.attributes)?
114                    .inner_html()?
115                    .write_html(self.inner_html)
116                    .map(|_| ())
117            }
118        }
119    }
120}
121
122// TODO comment tag
123
124// TODO see https://www.w3schools.com/tags/
125
126/// Creates a new custom tag.
127///
128/// See [`Tag`] for more information.
129///
130/// # Arguments
131/// * `tag` - The name of the tag.
132/// * `attributes` - The attributes of the tag.
133/// * `inner_html` - The inner HTML of the tag.
134/// * `compactability` - Whether the tag can be compacted.
135pub fn tag<'t, A: Attributes, I: Html>(
136    tag: &'t str,
137    attributes: A,
138    inner_html: I,
139    compactability: Compactability,
140) -> Tag<'t, A, I, false> {
141    Tag {
142        tag,
143        attributes,
144        inner_html,
145        compactability,
146    }
147}
148
149/// Add a node whose tags are not rendered.
150///
151/// TODO better docs, see https://react.dev/reference/react/Fragment
152pub fn fragment<I: Html>(
153    inner_html: I,
154) -> Tag<'static, Empty, I, true> {
155    Tag {
156        tag: "",
157        attributes: Empty,
158        inner_html,
159        compactability: Compactability::No,
160    }
161}
162
163// see https://stackoverflow.com/questions/41361897/documenting-a-function-created-with-a-macro-in-rust
164macro_rules! define_tag {
165    ($(#[$attr:meta])* $tag:ident $compactability:expr) => {
166/// Creates a new
167$(#[$attr])*
168/// tag.
169///
170/// See [`tag`] for more information.
171pub fn $tag(
172    attributes: impl Attributes,
173    inner_html: impl Html,
174) -> Tag<'static, impl Attributes, impl Html, false> {
175    tag(
176        stringify!($tag),
177        attributes,
178        inner_html,
179        $compactability,
180    )
181}
182    };
183}
184
185
186
187define_tag!(
188    /// [`<a>`](https://www.w3schools.com/tags/tag_a.asp)
189    a
190    Compactability::No
191);
192
193define_tag!(
194    /// [`<abbr>`](https://www.w3schools.com/tags/tag_abbr.asp)
195    abbr
196    Compactability::No
197);
198
199define_tag!(
200    /// [`<address>`](https://www.w3schools.com/tags/tag_address.asp)
201    address
202    Compactability::No
203);
204
205define_tag!(
206    /// [`<area>`](https://www.w3schools.com/tags/tag_area.asp)
207    area
208    Compactability::Yes { final_slash: false }
209);
210
211define_tag!(
212    /// [`<article>`](https://www.w3schools.com/tags/tag_article.asp)
213    article
214    Compactability::No
215);
216
217define_tag!(
218    /// [`<aside>`](https://www.w3schools.com/tags/tag_aside.asp)
219    aside
220    Compactability::No
221);
222
223define_tag!(
224    /// [`<audio>`](https://www.w3schools.com/tags/tag_audio.asp)
225    audio
226    Compactability::No
227);
228
229define_tag!(
230    /// [`<b>`](https://www.w3schools.com/tags/tag_b.asp)
231    b
232    Compactability::No
233);
234
235define_tag!(
236    /// [`<base>`](https://www.w3schools.com/tags/tag_base.asp)
237    base
238    Compactability::Yes { final_slash: false }
239);
240
241define_tag!(
242    /// [`<bdi>`](https://www.w3schools.com/tags/tag_bdi.asp)
243    bdi
244    Compactability::No
245);
246
247define_tag!(
248    /// [`<bdo>`](https://www.w3schools.com/tags/tag_bdo.asp)
249    bdo
250    Compactability::No
251);
252
253define_tag!(
254    /// [`<blockquote>`](https://www.w3schools.com/tags/tag_blockquote.asp)
255    blockquote
256    Compactability::No
257);
258
259define_tag!(
260    /// [`<body>`](https://www.w3schools.com/tags/tag_body.asp)
261    body
262    Compactability::No
263);
264
265define_tag!(
266    /// [`<br>`](https://www.w3schools.com/tags/tag_br.asp)
267    br
268    Compactability::Yes { final_slash: false }
269);
270
271define_tag!(
272    /// [`<button>`](https://www.w3schools.com/tags/tag_button.asp)
273    button
274    Compactability::No
275);
276
277define_tag!(
278    /// [`<canvas>`](https://www.w3schools.com/tags/tag_canvas.asp)
279    canvas
280    Compactability::No
281);
282
283define_tag!(
284    /// [`<caption>`](https://www.w3schools.com/tags/tag_caption.asp)
285    caption
286    Compactability::No
287);
288
289define_tag!(
290    /// [`<cite>`](https://www.w3schools.com/tags/tag_cite.asp)
291    cite
292    Compactability::No
293);
294
295define_tag!(
296    /// [`<code>`](https://www.w3schools.com/tags/tag_code.asp)
297    code
298    Compactability::No
299);
300
301define_tag!(
302    /// [`<col>`](https://www.w3schools.com/tags/tag_col.asp)
303    col
304    Compactability::Yes { final_slash: false }
305);
306
307define_tag!(
308    /// [`<colgroup>`](https://www.w3schools.com/tags/tag_colgroup.asp)
309    colgroup
310    Compactability::No
311);
312
313define_tag!(
314    /// [`<data>`](https://www.w3schools.com/tags/tag_data.asp)
315    data
316    Compactability::No
317);
318
319define_tag!(
320    /// [`<datalist>`](https://www.w3schools.com/tags/tag_datalist.asp)
321    datalist
322    Compactability::No
323);
324
325define_tag!(
326    /// [`<dd>`](https://www.w3schools.com/tags/tag_dd.asp)
327    dd
328    Compactability::No
329);
330
331define_tag!(
332    /// [`<del>`](https://www.w3schools.com/tags/tag_del.asp)
333    del
334    Compactability::No
335);
336
337define_tag!(
338    /// [`<details>`](https://www.w3schools.com/tags/tag_details.asp)
339    details
340    Compactability::No
341);
342
343define_tag!(
344    /// [`<dfn>`](https://www.w3schools.com/tags/tag_dfn.asp)
345    dfn
346    Compactability::No
347);
348
349define_tag!(
350    /// [`<dialog>`](https://www.w3schools.com/tags/tag_dialog.asp)
351    dialog
352    Compactability::No
353);
354
355define_tag!(
356    /// [`<div>`](https://www.w3schools.com/tags/tag_div.asp)
357    div
358    Compactability::No
359);
360
361define_tag!(
362    /// [`<dl>`](https://www.w3schools.com/tags/tag_dl.asp)
363    dl
364    Compactability::No
365);
366
367define_tag!(
368    /// [`<dt>`](https://www.w3schools.com/tags/tag_dt.asp)
369    dt
370    Compactability::No
371);
372
373define_tag!(
374    /// [`<em>`](https://www.w3schools.com/tags/tag_em.asp)
375    em
376    Compactability::No
377);
378
379define_tag!(
380    /// [`<embed>`](https://www.w3schools.com/tags/tag_embed.asp)
381    embed
382    Compactability::Yes { final_slash: false }
383);
384
385define_tag!(
386    /// [`<fieldset>`](https://www.w3schools.com/tags/tag_fieldset.asp)
387    fieldset
388    Compactability::No
389);
390
391define_tag!(
392    /// [`<figcaption>`](https://www.w3schools.com/tags/tag_figcaption.asp)
393    figcaption
394    Compactability::No
395);
396
397define_tag!(
398    /// [`<figure>`](https://www.w3schools.com/tags/tag_figure.asp)
399    figure
400    Compactability::No
401);
402
403define_tag!(
404    /// [`<footer>`](https://www.w3schools.com/tags/tag_footer.asp)
405    footer
406    Compactability::No
407);
408
409define_tag!(
410    /// [`<form>`](https://www.w3schools.com/tags/tag_form.asp)
411    form
412    Compactability::No
413);
414
415define_tag!(
416    /// [`<h1>`](https://www.w3schools.com/tags/tag_hn.asp)
417    h1
418    Compactability::No
419);
420
421define_tag!(
422    /// [`<h2>`](https://www.w3schools.com/tags/tag_hn.asp)
423    h2
424    Compactability::No
425);
426
427define_tag!(
428    /// [`<h3>`](https://www.w3schools.com/tags/tag_hn.asp)
429    h3
430    Compactability::No
431);
432
433define_tag!(
434    /// [`<h4>`](https://www.w3schools.com/tags/tag_hn.asp)
435    h4
436    Compactability::No
437);
438
439define_tag!(
440    /// [`<h5>`](https://www.w3schools.com/tags/tag_hn.asp)
441    h5
442    Compactability::No
443);
444
445define_tag!(
446    /// [`<h6>`](https://www.w3schools.com/tags/tag_hn.asp)
447    h6
448    Compactability::No
449);
450
451define_tag!(
452    /// [`<head>`](https://www.w3schools.com/tags/tag_head.asp)
453    head
454    Compactability::No
455);
456
457define_tag!(
458    /// [`<header>`](https://www.w3schools.com/tags/tag_header.asp)
459    header
460    Compactability::No
461);
462
463define_tag!(
464    /// [`<hgroup>`](https://www.w3schools.com/tags/tag_hgroup.asp)
465    hgroup
466    Compactability::No
467);
468
469define_tag!(
470    /// [`<hr>`](https://www.w3schools.com/tags/tag_hr.asp)
471    hr
472    Compactability::Yes { final_slash: false }
473);
474
475define_tag!(
476    /// [`<html>`](https://www.w3schools.com/tags/tag_html.asp)
477    html
478    Compactability::No
479);
480
481define_tag!(
482    /// [`<i>`](https://www.w3schools.com/tags/tag_i.asp)
483    i
484    Compactability::No
485);
486
487define_tag!(
488    /// [`<iframe>`](https://www.w3schools.com/tags/tag_iframe.asp)
489    iframe
490    Compactability::No
491);
492
493define_tag!(
494    /// [`<img>`](https://www.w3schools.com/tags/tag_img.asp)
495    img
496    Compactability::Yes { final_slash: false }
497);
498
499define_tag!(
500    /// [`<input>`](https://www.w3schools.com/tags/tag_input.asp)
501    input
502    Compactability::Yes { final_slash: false }
503);
504
505define_tag!(
506    /// [`<ins>`](https://www.w3schools.com/tags/tag_ins.asp)
507    ins
508    Compactability::No
509);
510
511define_tag!(
512    /// [`<kbd>`](https://www.w3schools.com/tags/tag_kbd.asp)
513    kbd
514    Compactability::No
515);
516
517define_tag!(
518    /// [`<label>`](https://www.w3schools.com/tags/tag_label.asp)
519    label
520    Compactability::No
521);
522
523define_tag!(
524    /// [`<legend>`](https://www.w3schools.com/tags/tag_legend.asp)
525    legend
526    Compactability::No
527);
528
529define_tag!(
530    /// [`<li>`](https://www.w3schools.com/tags/tag_li.asp)
531    li
532    Compactability::No
533);
534
535define_tag!(
536    /// [`<link>`](https://www.w3schools.com/tags/tag_link.asp)
537    link
538    Compactability::Yes { final_slash: false }
539);
540
541define_tag!(
542    /// [`<main>`](https://www.w3schools.com/tags/tag_main.asp)
543    main
544    Compactability::No
545);
546
547define_tag!(
548    /// [`<map>`](https://www.w3schools.com/tags/tag_map.asp)
549    map
550    Compactability::No
551);
552
553define_tag!(
554    /// [`<mark>`](https://www.w3schools.com/tags/tag_mark.asp)
555    mark
556    Compactability::No
557);
558
559define_tag!(
560    /// [`<meta>`](https://www.w3schools.com/tags/tag_meta.asp)
561    meta
562    Compactability::Yes { final_slash: false }
563);
564
565define_tag!(
566    /// [`<nav>`](https://www.w3schools.com/tags/tag_nav.asp)
567    nav
568    Compactability::No
569);
570
571define_tag!(
572    /// [`<noscript>`](https://www.w3schools.com/tags/tag_noscript.asp)
573    noscript
574    Compactability::No
575);
576
577define_tag!(
578    /// [`<object>`](https://www.w3schools.com/tags/tag_object.asp)
579    object
580    Compactability::No
581);
582
583define_tag!(
584    /// [`<ol>`](https://www.w3schools.com/tags/tag_ol.asp)
585    ol
586    Compactability::No
587);
588
589define_tag!(
590    /// [`<optgroup>`](https://www.w3schools.com/tags/tag_optgroup.asp)
591    optgroup
592    Compactability::No
593);
594
595define_tag!(
596    /// [`<option>`](https://www.w3schools.com/tags/tag_option.asp)
597    option
598    Compactability::No
599);
600
601define_tag!(
602    /// [`<output>`](https://www.w3schools.com/tags/tag_output.asp)
603    output
604    Compactability::No
605);
606
607define_tag!(
608    /// [`<p>`](https://www.w3schools.com/tags/tag_p.asp)
609    p
610    Compactability::No
611);
612
613define_tag!(
614    /// [`<param>`](https://www.w3schools.com/tags/tag_param.asp)
615    param
616    Compactability::Yes { final_slash: false }
617);
618
619define_tag!(
620    /// [`<pre>`](https://www.w3schools.com/tags/tag_pre.asp)
621    pre
622    Compactability::No
623);
624
625define_tag!(
626    /// [`<progress>`](https://www.w3schools.com/tags/tag_progress.asp)
627    progress
628    Compactability::No
629);
630
631define_tag!(
632    /// [`<q>`](https://www.w3schools.com/tags/tag_q.asp)
633    q
634    Compactability::No
635);
636
637define_tag!(
638    /// [`<rp>`](https://www.w3schools.com/tags/tag_rp.asp)
639    rp
640    Compactability::No
641);
642
643define_tag!(
644    /// [`<rt>`](https://www.w3schools.com/tags/tag_rt.asp)
645    rt
646    Compactability::No
647);
648
649define_tag!(
650    /// [`<ruby>`](https://www.w3schools.com/tags/tag_ruby.asp)
651    ruby
652    Compactability::No
653);
654
655define_tag!(
656    /// [`<s>`](https://www.w3schools.com/tags/tag_s.asp)
657    s
658    Compactability::No
659);
660
661define_tag!(
662    /// [`<samp>`](https://www.w3schools.com/tags/tag_samp.asp)
663    samp
664    Compactability::No
665);
666
667define_tag!(
668    /// [`<script>`](https://www.w3schools.com/tags/tag_script.asp)
669    script
670    Compactability::No
671);
672
673define_tag!(
674    /// [`<section>`](https://www.w3schools.com/tags/tag_section.asp)
675    section
676    Compactability::No
677);
678
679define_tag!(
680    /// [`<select>`](https://www.w3schools.com/tags/tag_select.asp)
681    select
682    Compactability::No
683);
684
685define_tag!(
686    /// [`<small>`](https://www.w3schools.com/tags/tag_small.asp)
687    small
688    Compactability::No
689);
690
691define_tag!(
692    /// [`<source>`](https://www.w3schools.com/tags/tag_source.asp)
693    source
694    Compactability::Yes { final_slash: false }
695);
696
697define_tag!(
698    /// [`<span>`](https://www.w3schools.com/tags/tag_span.asp)
699    span
700    Compactability::No
701);
702
703define_tag!(
704    /// [`<strong>`](https://www.w3schools.com/tags/tag_strong.asp)
705    strong
706    Compactability::No
707);
708
709define_tag!(
710    /// [`<style>`](https://www.w3schools.com/tags/tag_style.asp)
711    style
712    Compactability::No
713);
714
715define_tag!(
716    /// [`<sub>`](https://www.w3schools.com/tags/tag_sub.asp)
717    sub
718    Compactability::No
719);
720
721define_tag!(
722    /// [`<summary>`](https://www.w3schools.com/tags/tag_summary.asp)
723    summary
724    Compactability::No
725);
726
727define_tag!(
728    /// [`<sup>`](https://www.w3schools.com/tags/tag_sup.asp)
729    sup
730    Compactability::No
731);
732
733define_tag!(
734    /// [`<table>`](https://www.w3schools.com/tags/tag_table.asp)
735    table
736    Compactability::No
737);
738
739define_tag!(
740    /// [`<tbody>`](https://www.w3schools.com/tags/tag_tbody.asp)
741    tbody
742    Compactability::No
743);
744
745define_tag!(
746    /// [`<td>`](https://www.w3schools.com/tags/tag_td.asp)
747    td
748    Compactability::No
749);
750
751define_tag!(
752    /// [`<template>`](https://www.w3schools.com/tags/tag_template.asp)
753    template
754    Compactability::No
755);
756
757define_tag!(
758    /// [`<textarea>`](https://www.w3schools.com/tags/tag_textarea.asp)
759    textarea
760    Compactability::No
761);
762
763define_tag!(
764    /// [`<tfoot>`](https://www.w3schools.com/tags/tag_tfoot.asp)
765    tfoot
766    Compactability::No
767);
768
769define_tag!(
770    /// [`<th>`](https://www.w3schools.com/tags/tag_th.asp)
771    th
772    Compactability::No
773);
774
775define_tag!(
776    /// [`<thead>`](https://www.w3schools.com/tags/tag_thead.asp)
777    thead
778    Compactability::No
779);
780
781define_tag!(
782    /// [`<time>`](https://www.w3schools.com/tags/tag_time.asp)
783    time
784    Compactability::No
785);
786
787define_tag!(
788    /// [`<title>`](https://www.w3schools.com/tags/tag_title.asp)
789    title
790    Compactability::No
791);
792
793define_tag!(
794    /// [`<tr>`](https://www.w3schools.com/tags/tag_tr.asp)
795    tr
796    Compactability::No
797);
798
799define_tag!(
800    /// [`<track>`](https://www.w3schools.com/tags/tag_track.asp)
801    track
802    Compactability::Yes { final_slash: false }
803);
804
805define_tag!(
806    /// [`<u>`](https://www.w3schools.com/tags/tag_u.asp)
807    u
808    Compactability::No
809);
810
811define_tag!(
812    /// [`<ul>`](https://www.w3schools.com/tags/tag_ul.asp)
813    ul
814    Compactability::No
815);
816
817define_tag!(
818    /// [`<var>`](https://www.w3schools.com/tags/tag_var.asp)
819    var
820    Compactability::No
821);
822
823define_tag!(
824    /// [`<video>`](https://www.w3schools.com/tags/tag_video.asp)
825    video
826    Compactability::No
827);
828
829define_tag!(
830    /// [`<wbr>`](https://www.w3schools.com/tags/tag_wbr.asp)
831    wbr
832    Compactability::Yes { final_slash: false }
833);