sailfish/runtime/
render.rs

1use std::borrow::Cow;
2use std::cell::{Ref, RefMut};
3use std::fmt;
4use std::num::{
5    NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize,
6    NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
7};
8use std::path::{Path, PathBuf};
9use std::rc::Rc;
10use std::sync::{Arc, MutexGuard, RwLockReadGuard, RwLockWriteGuard};
11
12use super::buffer::Buffer;
13use super::escape;
14
15/// types which can be rendered inside buffer block (`<%= %>`)
16///
17/// If you want to render the custom data, you must implement this trait and specify
18/// the behaviour.
19///
20/// # Safety
21///
22/// This trait allows modifying the previously-rendered contents or even decreasing the
23/// buffer size. However, such an operation easily cause unexpected rendering results.
24/// In order to avoid this, implementors should ensure that the contents which is already
25/// rendered won't be changed during `render` or `render_escaped` method is called.
26///
27/// # Examples
28///
29/// ```
30/// use sailfish::runtime::{Buffer, Render, RenderError};
31///
32/// struct MyU64(u64);
33///
34/// impl Render for MyU64 {
35///     #[inline]
36///     fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
37///         self.0.render(b)
38///     }
39/// }
40/// ```
41pub trait Render {
42    /// render to `Buffer` without escaping
43    fn render(&self, b: &mut Buffer) -> Result<(), RenderError>;
44
45    /// render to `Buffer` with HTML escaping
46    #[inline]
47    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
48        let mut tmp = Buffer::new();
49        self.render(&mut tmp)?;
50        escape::escape_to_buf(tmp.as_str(), b);
51        Ok(())
52    }
53}
54
55// /// Autoref-based stable specialization
56// ///
57// /// Explanation can be found [here](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md)
58// impl<T: Display> Render for &T {
59//     fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
60//         fmt::write(b, format_args!("{}", self))
61//     }
62//
63//     fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
64//         struct Wrapper<'a>(&'a mut Buffer);
65//
66//         impl<'a> fmt::Write for Wrapper<'a> {
67//             #[inline]
68//             fn push_str(&mut self, s: &str) -> Result<(), RenderError> {
69//                 escape::escape_to_buf(s, self.0);
70//                 Ok(())
71//             }
72//         }
73//
74//         fmt::write(&mut Wrapper(b), format_args!("{}", self))
75//     }
76// }
77
78impl Render for String {
79    #[inline]
80    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
81        b.push_str(self);
82        Ok(())
83    }
84
85    #[inline]
86    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
87        escape::escape_to_buf(self, b);
88        Ok(())
89    }
90}
91
92impl Render for str {
93    #[inline]
94    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
95        b.push_str(self);
96        Ok(())
97    }
98
99    #[inline]
100    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
101        escape::escape_to_buf(self, b);
102        Ok(())
103    }
104}
105
106impl Render for char {
107    #[inline]
108    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
109        b.push(*self);
110        Ok(())
111    }
112
113    #[inline]
114    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
115        match *self {
116            '\"' => b.push_str("&quot;"),
117            '&' => b.push_str("&amp;"),
118            '<' => b.push_str("&lt;"),
119            '>' => b.push_str("&gt;"),
120            '\'' => b.push_str("&#039;"),
121            _ => b.push(*self),
122        }
123        Ok(())
124    }
125}
126
127impl Render for PathBuf {
128    #[inline]
129    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
130        // TODO: speed up on Windows using OsStrExt
131        b.push_str(&self.to_string_lossy());
132        Ok(())
133    }
134
135    #[inline]
136    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
137        escape::escape_to_buf(&self.to_string_lossy(), b);
138        Ok(())
139    }
140}
141
142impl Render for Path {
143    #[inline]
144    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
145        // TODO: speed up on Windows using OsStrExt
146        b.push_str(&self.to_string_lossy());
147        Ok(())
148    }
149
150    #[inline]
151    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
152        escape::escape_to_buf(&self.to_string_lossy(), b);
153        Ok(())
154    }
155}
156
157// impl Render for [u8] {
158//     #[inline]
159//     fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
160//         b.write_bytes(self);
161//         Ok(())
162//     }
163// }
164//
165// impl<'a> Render for &'a [u8] {
166//     #[inline]
167//     fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
168//         b.write_bytes(self);
169//         Ok(())
170//     }
171// }
172//
173// impl Render for Vec<u8> {
174//     #[inline]
175//     fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
176//         b.write_bytes(&**self);
177//         Ok(())
178//     }
179// }
180
181impl Render for bool {
182    #[inline]
183    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
184        let s = if *self { "true" } else { "false" };
185        b.push_str(s);
186        Ok(())
187    }
188
189    #[inline]
190    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
191        self.render(b)
192    }
193}
194
195macro_rules! render_int {
196    ($($int:ty),*) => {
197        $(
198            impl Render for $int {
199                #[cfg_attr(feature = "perf-inline", inline)]
200                fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
201                    use itoap::Integer;
202
203                    // SAFETY: `MAX_LEN < 40` and then does not overflows `isize::MAX`.
204                    // Also `b.len()` should be always less than or equal to `isize::MAX`.
205                    unsafe {
206                        b.reserve_small(Self::MAX_LEN);
207                        let ptr = b.as_mut_ptr().add(b.len());
208
209                        // SAFETY: `MAX_LEN` is always greater than zero, so
210                        // `b.as_mut_ptr()` always point to valid block of memory
211                        let l = itoap::write_to_ptr(ptr, *self);
212                        b.advance(l);
213                    }
214                    debug_assert!(b.len() <= b.capacity());
215                    Ok(())
216                }
217
218                #[inline]
219                fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
220                    // push_str without escape
221                    self.render(b)
222                }
223            }
224        )*
225    }
226}
227
228render_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
229
230impl Render for f32 {
231    #[cfg_attr(feature = "perf-inline", inline)]
232    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
233        if likely!(self.is_finite()) {
234            unsafe {
235                b.reserve_small(16);
236                let ptr = b.as_mut_ptr().add(b.len());
237                let l = ryu::raw::format32(*self, ptr);
238                b.advance(l);
239                debug_assert!(b.len() <= b.capacity());
240            }
241        } else if self.is_nan() {
242            b.push_str("NaN");
243        } else if *self > 0.0 {
244            b.push_str("inf");
245        } else {
246            b.push_str("-inf");
247        }
248
249        Ok(())
250    }
251
252    #[inline]
253    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
254        // escape string
255        self.render(b)
256    }
257}
258
259impl Render for f64 {
260    #[cfg_attr(feature = "perf-inline", inline)]
261    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
262        if likely!(self.is_finite()) {
263            unsafe {
264                b.reserve_small(24);
265                let ptr = b.as_mut_ptr().add(b.len());
266                let l = ryu::raw::format64(*self, ptr);
267                b.advance(l);
268                debug_assert!(b.len() <= b.capacity());
269            }
270        } else if self.is_nan() {
271            b.push_str("NaN");
272        } else if *self > 0.0 {
273            b.push_str("inf");
274        } else {
275            b.push_str("-inf");
276        }
277
278        Ok(())
279    }
280
281    #[inline]
282    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
283        // escape string
284        self.render(b)
285    }
286}
287
288macro_rules! render_deref {
289    (
290        $(#[doc = $doc:tt])*
291        [$($bounds:tt)+] $($desc:tt)+
292    ) => {
293        $(#[doc = $doc])*
294        impl <$($bounds)+> Render for $($desc)+ {
295            #[inline]
296            fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
297                (**self).render(b)
298            }
299
300            #[inline]
301            fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
302                (**self).render_escaped(b)
303            }
304        }
305    };
306}
307
308render_deref!(['a, T: Render + ?Sized] &'a T);
309render_deref!(['a, T: Render + ?Sized] &'a mut T);
310render_deref!([T: Render + ?Sized] Box<T>);
311render_deref!([T: Render + ?Sized] Rc<T>);
312render_deref!([T: Render + ?Sized] Arc<T>);
313render_deref!(['a, T: Render + ToOwned + ?Sized] Cow<'a, T>);
314render_deref!(['a, T: Render + ?Sized] Ref<'a, T>);
315render_deref!(['a, T: Render + ?Sized] RefMut<'a, T>);
316render_deref!(['a, T: Render + ?Sized] MutexGuard<'a, T>);
317render_deref!(['a, T: Render + ?Sized] RwLockReadGuard<'a, T>);
318render_deref!(['a, T: Render + ?Sized] RwLockWriteGuard<'a, T>);
319
320macro_rules! render_nonzero {
321    ($($type:ty,)*) => {
322        $(
323            impl Render for $type {
324                #[inline]
325                fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
326                    self.get().render(b)
327                }
328
329                #[inline]
330                fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
331                    self.get().render_escaped(b)
332                }
333            }
334        )*
335    }
336}
337
338render_nonzero!(
339    NonZeroI8,
340    NonZeroI16,
341    NonZeroI32,
342    NonZeroI64,
343    NonZeroI128,
344    NonZeroIsize,
345    NonZeroU8,
346    NonZeroU16,
347    NonZeroU32,
348    NonZeroU64,
349    NonZeroU128,
350    NonZeroUsize,
351);
352
353impl<T: Render> Render for Wrapping<T> {
354    #[inline]
355    fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
356        self.0.render(b)
357    }
358
359    #[inline]
360    fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
361        self.0.render_escaped(b)
362    }
363}
364
365/// The error type which is returned from template function
366#[derive(Clone, Debug)]
367pub enum RenderError {
368    /// Custom error message
369    Msg(String),
370    /// fmt::Error was raised during rendering
371    Fmt(fmt::Error),
372    /// Buffer size shrinked during rendering
373    ///
374    /// This method won't be raised unless you implement `Render` trait for custom type.
375    ///
376    /// Also there is no guarentee that this error will be returned whenever the buffer
377    /// size shrinked.
378    BufSize,
379}
380
381impl RenderError {
382    /// Construct a new error with custom message
383    pub fn new(msg: &str) -> Self {
384        RenderError::Msg(msg.to_owned())
385    }
386}
387
388impl fmt::Display for RenderError {
389    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
390        match self {
391            RenderError::Msg(s) => f.pad(s),
392            RenderError::Fmt(e) => fmt::Display::fmt(e, f),
393            RenderError::BufSize => f.pad("buffer size shrinked while rendering"),
394        }
395    }
396}
397
398impl std::error::Error for RenderError {
399    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
400        match self {
401            RenderError::Msg(_) | RenderError::BufSize => None,
402            RenderError::Fmt(e) => Some(e),
403        }
404    }
405}
406
407impl From<fmt::Error> for RenderError {
408    #[inline]
409    fn from(other: fmt::Error) -> Self {
410        RenderError::Fmt(other)
411    }
412}
413
414/// Result type returned from `TemplateOnce::render_once` method
415pub type RenderResult = Result<String, RenderError>;
416
417#[cfg(test)]
418mod tests {
419    use super::*;
420    use std::error::Error;
421
422    #[test]
423    fn receiver_coercion() {
424        let mut b = Buffer::new();
425        Render::render(&1, &mut b).unwrap();
426        Render::render(&&1, &mut b).unwrap();
427        Render::render(&&&1, &mut b).unwrap();
428        Render::render(&&&&1, &mut b).unwrap();
429        assert_eq!(b.as_str(), "1111");
430        b.clear();
431
432        Render::render(&true, &mut b).unwrap();
433        Render::render(&&false, &mut b).unwrap();
434        Render::render_escaped(&&&true, &mut b).unwrap();
435        Render::render_escaped(&&&&false, &mut b).unwrap();
436        assert_eq!(b.as_str(), "truefalsetruefalse");
437        b.clear();
438
439        let s = "apple";
440        Render::render_escaped(&s, &mut b).unwrap();
441        Render::render_escaped(&s, &mut b).unwrap();
442        Render::render_escaped(&&s, &mut b).unwrap();
443        Render::render_escaped(&&&s, &mut b).unwrap();
444        Render::render_escaped(&&&&s, &mut b).unwrap();
445        assert_eq!(b.as_str(), "appleappleappleappleapple");
446        b.clear();
447
448        Render::render_escaped(&'c', &mut b).unwrap();
449        Render::render_escaped(&&'<', &mut b).unwrap();
450        Render::render_escaped(&&&'&', &mut b).unwrap();
451        Render::render_escaped(&&&&' ', &mut b).unwrap();
452        assert_eq!(b.as_str(), "c&lt;&amp; ");
453        b.clear();
454    }
455
456    #[test]
457    fn deref_coercion() {
458        use std::path::{Path, PathBuf};
459        use std::rc::Rc;
460
461        let mut b = Buffer::new();
462        Render::render(&String::from("a"), &mut b).unwrap();
463        Render::render(&&PathBuf::from("b"), &mut b).unwrap();
464        Render::render_escaped(&Rc::new(4u32), &mut b).unwrap();
465        Render::render_escaped(&Rc::new(2.3f32), &mut b).unwrap();
466        Render::render_escaped(Path::new("<"), &mut b).unwrap();
467        Render::render_escaped(&Path::new("d"), &mut b).unwrap();
468
469        assert_eq!(b.as_str(), "ab42.3&lt;d");
470    }
471
472    #[test]
473    fn float() {
474        let mut b = Buffer::new();
475
476        Render::render_escaped(&0.0f64, &mut b).unwrap();
477        Render::render_escaped(&std::f64::INFINITY, &mut b).unwrap();
478        Render::render_escaped(&std::f64::NEG_INFINITY, &mut b).unwrap();
479        Render::render_escaped(&std::f64::NAN, &mut b).unwrap();
480        assert_eq!(b.as_str(), "0.0inf-infNaN");
481        b.clear();
482
483        Render::render_escaped(&0.0f32, &mut b).unwrap();
484        Render::render_escaped(&std::f32::INFINITY, &mut b).unwrap();
485        Render::render_escaped(&std::f32::NEG_INFINITY, &mut b).unwrap();
486        Render::render_escaped(&std::f32::NAN, &mut b).unwrap();
487        assert_eq!(b.as_str(), "0.0inf-infNaN");
488    }
489
490    #[test]
491    fn test_char() {
492        let mut b = Buffer::new();
493
494        let funcs: Vec<fn(&char, &mut Buffer) -> Result<(), RenderError>> =
495            vec![Render::render, Render::render_escaped];
496
497        for func in funcs {
498            func(&'a', &mut b).unwrap();
499            func(&'b', &mut b).unwrap();
500            func(&'c', &mut b).unwrap();
501            func(&'d', &mut b).unwrap();
502
503            assert_eq!(b.as_str(), "abcd");
504            b.clear();
505
506            func(&'あ', &mut b).unwrap();
507            func(&'い', &mut b).unwrap();
508            func(&'う', &mut b).unwrap();
509            func(&'え', &mut b).unwrap();
510
511            assert_eq!(b.as_str(), "あいうえ");
512            b.clear();
513        }
514    }
515
516    #[test]
517    fn test_nonzero() {
518        let mut b = Buffer::with_capacity(2);
519        Render::render(&NonZeroU8::new(10).unwrap(), &mut b).unwrap();
520        Render::render_escaped(&NonZeroI16::new(-20).unwrap(), &mut b).unwrap();
521        assert_eq!(b.as_str(), "10-20");
522    }
523
524    #[test]
525    fn render_error() {
526        let err = RenderError::new("custom error");
527        assert!(err.source().is_none());
528        assert_eq!(format!("{}", err), "custom error");
529
530        let err = RenderError::from(std::fmt::Error::default());
531        assert!(err.source().is_some());
532        assert_eq!(
533            format!("{}", err),
534            format!("{}", std::fmt::Error::default())
535        );
536
537        let err = RenderError::BufSize;
538        assert!(err.source().is_none());
539
540        assert!(format!("{}", err).is_empty().eq(&false));
541    }
542}