markup/
lib.rs

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}