1use std::fmt::Write;
2
3pub use markup_proc_macro::{define, new};
4
5mod escape;
6
7pub trait Render {
8 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result;
9}
10
11pub trait RenderAttributeValue: Render {
12 #[inline]
13 fn is_none(&self) -> bool {
14 false
15 }
16
17 #[inline]
18 fn is_true(&self) -> bool {
19 false
20 }
21
22 #[inline]
23 fn is_false(&self) -> bool {
24 false
25 }
26}
27
28impl<'a, T: Render + ?Sized> Render for &'a T {
29 #[inline]
30 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
31 T::render(self, writer)
32 }
33}
34
35impl<'a, T: RenderAttributeValue + ?Sized> RenderAttributeValue for &'a T {
36 #[inline]
37 fn is_none(&self) -> bool {
38 T::is_none(self)
39 }
40
41 #[inline]
42 fn is_true(&self) -> bool {
43 T::is_true(self)
44 }
45
46 #[inline]
47 fn is_false(&self) -> bool {
48 T::is_false(self)
49 }
50}
51
52impl<T: Render + ?Sized> Render for Box<T> {
53 #[inline]
54 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
55 T::render(self, writer)
56 }
57}
58
59impl<T: RenderAttributeValue + ?Sized> RenderAttributeValue for Box<T> {
60 #[inline]
61 fn is_none(&self) -> bool {
62 T::is_none(self)
63 }
64
65 #[inline]
66 fn is_true(&self) -> bool {
67 T::is_true(self)
68 }
69
70 #[inline]
71 fn is_false(&self) -> bool {
72 T::is_false(self)
73 }
74}
75
76impl Render for bool {
77 #[inline]
78 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
79 write!(writer, "{}", self)
80 }
81}
82
83impl RenderAttributeValue for bool {
84 #[inline]
85 fn is_true(&self) -> bool {
86 *self
87 }
88
89 #[inline]
90 fn is_false(&self) -> bool {
91 !self
92 }
93}
94
95impl<T: Render> Render for Option<T> {
96 #[inline]
97 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
98 match self {
99 Some(t) => t.render(writer),
100 None => Ok(()),
101 }
102 }
103}
104
105impl<T: RenderAttributeValue> RenderAttributeValue for Option<T> {
106 #[inline]
107 fn is_none(&self) -> bool {
108 self.is_none()
109 }
110}
111
112struct Raw<T: std::fmt::Display>(T);
113
114impl<T: std::fmt::Display> Render for Raw<T> {
115 #[inline]
116 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
117 write!(writer, "{}", self.0)
118 }
119}
120
121impl<T: std::fmt::Display> RenderAttributeValue for Raw<T> {}
122
123#[inline]
124pub fn raw(value: impl std::fmt::Display) -> impl Render + RenderAttributeValue {
125 Raw(value)
126}
127
128macro_rules! tfor {
129 (for $ty:ident in [$($typ:ident),*] $tt:tt) => {
130 $( const _: () = { type $ty = $typ; tfor! { @extract $tt } }; )*
131 };
132 (@extract { $($tt:tt)* }) => { $($tt)* };
133}
134
135tfor! {
136 for Ty in [char, f32, f64] {
137 impl Render for Ty {
138 #[inline]
139 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
140 write!(writer, "{}", self)
141 }
142 }
143
144 impl RenderAttributeValue for Ty {
145 }
146 }
147}
148
149tfor! {
150 for Ty in [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize] {
151 impl Render for Ty {
152 #[inline]
153 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
154 #[cfg(feature = "itoa")] {
155 let mut buffer = itoa::Buffer::new();
156 let str = buffer.format(*self);
157 writer.write_str(str)
158 }
159 #[cfg(not(feature = "itoa"))] {
160 write!(writer, "{}", self)
161 }
162 }
163 }
164
165 impl RenderAttributeValue for Ty {
166 }
167 }
168}
169
170impl Render for str {
171 #[inline]
172 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
173 escape::escape(self, writer)
174 }
175}
176
177impl RenderAttributeValue for str {}
178
179impl Render for String {
180 #[inline]
181 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
182 self.as_str().render(writer)
183 }
184}
185
186impl RenderAttributeValue for String {}
187
188impl Render for std::fmt::Arguments<'_> {
189 #[inline]
190 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
191 escape::Escape(writer).write_fmt(*self)
192 }
193}
194
195impl RenderAttributeValue for std::fmt::Arguments<'_> {}
196
197macro_rules! tuple_impl {
198 ($($ident:ident)+) => {
199 impl<$($ident: Render,)+> Render for ($($ident,)+) {
200 #[allow(non_snake_case)]
201 #[inline]
202 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
203 let ($(ref $ident,)+) = *self;
204 $($ident.render(writer)?;)+
205 Ok(())
206 }
207 }
208
209 impl<$($ident: RenderAttributeValue,)+> RenderAttributeValue for ($($ident,)+) {
210 }
211 }
212}
213
214tuple_impl! { A }
215tuple_impl! { A B }
216tuple_impl! { A B C }
217tuple_impl! { A B C D }
218tuple_impl! { A B C D E }
219tuple_impl! { A B C D E F }
220tuple_impl! { A B C D E F G }
221tuple_impl! { A B C D E F G H }
222tuple_impl! { A B C D E F G H I }
223tuple_impl! { A B C D E F G H I J }
224
225pub struct DynRender<'a> {
226 f: Box<dyn Fn(&mut dyn std::fmt::Write) -> std::fmt::Result + 'a>,
227}
228
229pub fn new<'a, F>(f: F) -> DynRender<'a>
230where
231 F: Fn(&mut dyn std::fmt::Write) -> std::fmt::Result + 'a,
232{
233 DynRender { f: Box::new(f) }
234}
235
236impl<'a> Render for DynRender<'a> {
237 #[inline]
238 fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
239 (self.f)(writer)
240 }
241}
242
243impl<'a> std::fmt::Display for DynRender<'a> {
244 #[inline]
245 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
246 Render::render(self, fmt)
247 }
248}
249
250#[inline]
251pub fn doctype() -> impl Render {
252 raw("<!DOCTYPE html>")
253}