1use super::Nesting;
4use std::{
5 fmt::{self, Display, Formatter, Write},
6 ops::Deref,
7};
8
9pub const ESCAPED_TEXT_CHARACTERS: [(char, &str); 3] =
11 [('<', "<"), ('>', ">"), ('&', "&")];
12
13pub trait Formattable {
15 #[allow(clippy::missing_errors_doc)]
17 fn format(&self, formatter: &mut Formatter, _: Nesting) -> fmt::Result;
18}
19
20impl_primitives!(Formattable);
21impl_tuples!(Formattable);
22
23impl Formattable for char {
24 fn format(&self, formatter: &mut Formatter, _: Nesting) -> fmt::Result {
25 if let Some((_, escaped)) =
26 ESCAPED_TEXT_CHARACTERS.iter().find(|(c, _)| *c == *self)
27 {
28 formatter.write_str(escaped)
29 } else {
30 formatter.write_char(*self)
31 }
32 }
33}
34
35impl Formattable for &'_ str {
36 fn format(
37 &self,
38 formatter: &mut Formatter,
39 nesting: Nesting,
40 ) -> fmt::Result {
41 self.chars()
42 .map(|character| character.format(formatter, nesting))
43 .collect()
44 }
45}
46
47impl Formattable for String {
48 fn format(
49 &self,
50 formatter: &mut Formatter,
51 nesting: Nesting,
52 ) -> fmt::Result {
53 self.as_str().format(formatter, nesting)
54 }
55}
56
57impl<T: Formattable> Formattable for &'_ [T] {
58 fn format(
59 &self,
60 formatter: &mut Formatter,
61 nesting: Nesting,
62 ) -> fmt::Result {
63 self.iter().map(|x| x.format(formatter, nesting)).collect()
64 }
65}
66
67impl<T: Formattable> Formattable for Vec<T> {
68 fn format(
69 &self,
70 formatter: &mut Formatter,
71 nesting: Nesting,
72 ) -> fmt::Result {
73 self.as_slice().format(formatter, nesting)
74 }
75}
76
77impl<T: Formattable + ?Sized> Formattable for Box<T> {
78 fn format(
79 &self,
80 formatter: &mut Formatter,
81 nesting: Nesting,
82 ) -> fmt::Result {
83 self.deref().format(formatter, nesting)
84 }
85}
86
87#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
91#[must_use = "HTML needs to be turned into a String with `.to_string()`"]
92pub struct Html<T>(T);
93
94pub fn html<T: Formattable>(content: T) -> Html<T> {
96 Html(content)
97}
98
99impl<T: Formattable> Formattable for Html<T> {
100 fn format(
101 &self,
102 formatter: &mut Formatter,
103 nesting: Nesting,
104 ) -> fmt::Result {
105 self.0.format(formatter, nesting)
106 }
107}
108
109impl<T: Formattable> Display for Html<T> {
110 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
111 self.format(formatter, Nesting::default())
112 }
113}