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);