nate/
details.rs

1#[cfg(feature = "alloc")]
2extern crate alloc;
3#[cfg(feature = "std")]
4extern crate std;
5
6#[doc(hidden)]
7pub use core;
8use core::fmt;
9#[cfg(feature = "alloc")]
10use core::fmt::Write as _;
11use core::marker::PhantomData;
12
13pub use crate::escape::{EscapeKind, XmlEscape};
14pub use crate::fast_float::FloatKind;
15pub use crate::fast_integer::IntKind;
16pub use crate::raw::RawKind;
17
18/// [Zero sized](https://doc.rust-lang.org/1.56.0/nomicon/exotic-sizes.html#zero-sized-types-zsts)
19/// wrapper used to select an escape function
20///
21/// To implement your own specialization, you can implement your own trait this way:
22///
23/// ```rust,ignore
24/// use std::fmt;
25///
26/// // First you have to add a "marker" trait for types that you want to escape
27/// // with your custom escaper.
28///
29/// trait MyEscapeMarker {}
30///
31/// impl<T: MyEscapeMarker> MyEscapeMarker for &T {}
32///
33/// // You can implement your custom escaper for multiple types.
34///
35/// enum TerribleXml<'a> {
36///     Start(&'a str),
37///     End(&'a str),
38///     Text(&'a str),
39/// }
40///
41/// impl MyEscapeMarker for TerribleXml<'_> {}
42///
43/// // Second you add a new trait that wraps a reference to the value to escape.
44/// // If the value is `Copy`, then you don't have to keep reference to `value`.
45/// // You must not capture a reference to `self`, because `self` is ephemeral.
46///
47/// trait MyEscapeKind {
48///     #[inline]
49///     fn wrap<'a>(&self, value: &'a TerribleXml) -> MyEscaper<'a> {
50///         MyEscaper { value }
51///     }
52/// }
53///
54/// impl<T: MyEscapeMarker> MyEscapeKind for nate::EscapeWrapper<T> {}
55///
56/// // Lastly you have to implement `std::fmt::Display` for your escaper.
57///
58/// struct MyEscaper<'a> {
59///     value: &'a TerribleXml<'a>,
60/// }
61///
62/// impl fmt::Display for MyEscaper<'_> {
63///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64///         match self.value {
65///             TerribleXml::Start(tag) => write!(f, "<{}>", tag),
66///             TerribleXml::End(tag) => write!(f, "</{}>", tag),
67///             TerribleXml::Text(text) => f.write_str(text),
68///         }
69///     }
70/// }
71///
72/// // Then you can use the escaper in your templates.
73/// // The trait `MyEscapeKind` has to be in scope of the template declaration.
74///
75/// #[derive(nate::Nate)]
76/// #[template(path = "templates/custom-escaper.html")]
77/// struct Template<'a> {
78///     elems: &'a [TerribleXml<'a>],
79/// }
80///
81/// #[test]
82/// fn test_custom_escaper() {
83///     let template = Template { elems: &[
84///         TerribleXml::Text("Hello, "),
85///         TerribleXml::Start("strong"),
86///         TerribleXml::Text("world"),
87///         TerribleXml::End("b"),
88///         TerribleXml::Text("!"),
89///     ] };
90///     let data = format!("{}", template);
91///     assert_eq!(data, "Hello, <strong>world</b>!");
92/// }
93/// ```
94#[derive(Debug, Clone, Copy, Default)]
95pub struct EscapeWrapper<E>(PhantomData<E>);
96
97impl<E> EscapeWrapper<E> {
98    #[doc(hidden)]
99    #[inline]
100    pub fn new(_: &E) -> Self {
101        Self(PhantomData)
102    }
103}
104
105#[doc(hidden)]
106pub trait WriteAny {
107    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> fmt::Result;
108    fn write_str(&mut self, s: &str) -> fmt::Result;
109}
110
111#[cfg(feature = "std")]
112pub(crate) struct WriteIo<W: std::io::Write>(pub(crate) W);
113
114pub(crate) struct WriteFmt<W: fmt::Write>(pub(crate) W);
115
116#[cfg(feature = "alloc")]
117pub(crate) struct WriteString<'a>(pub(crate) &'a mut alloc::string::String);
118
119#[cfg(feature = "std")]
120impl<W: std::io::Write> WriteAny for WriteIo<W> {
121    #[inline]
122    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> fmt::Result {
123        match <W as std::io::Write>::write_fmt(&mut self.0, fmt) {
124            Ok(_) => Ok(()),
125            Err(_) => Err(fmt::Error),
126        }
127    }
128
129    #[inline]
130    fn write_str(&mut self, s: &str) -> fmt::Result {
131        self.write_fmt(format_args!("{}", s))
132    }
133}
134
135impl<W: fmt::Write> WriteAny for WriteFmt<W> {
136    #[inline]
137    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> fmt::Result {
138        <W as fmt::Write>::write_fmt(&mut self.0, fmt)
139    }
140
141    #[inline]
142    fn write_str(&mut self, s: &str) -> fmt::Result {
143        <W as fmt::Write>::write_str(&mut self.0, s)
144    }
145}
146
147#[cfg(feature = "alloc")]
148impl WriteAny for WriteString<'_> {
149    #[inline]
150    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> fmt::Result {
151        if let Some(s) = fmt.as_str() {
152            self.0.push_str(s);
153            Ok(())
154        } else {
155            self.0.write_fmt(fmt)
156        }
157    }
158
159    #[inline]
160    fn write_str(&mut self, s: &str) -> fmt::Result {
161        self.0.push_str(s);
162        Ok(())
163    }
164}
165
166/// Optimized trait methods to render a NaTE template
167///
168/// Every NaTE template implements this trait.
169pub trait RenderInto {
170    #[doc(hidden)]
171    fn render_into(&self, output: impl WriteAny) -> fmt::Result;
172
173    /// Render the output into an [`fmt::Write`](std::fmt::Write) object
174    #[inline]
175    fn render_fmt(&self, output: impl fmt::Write) -> fmt::Result {
176        self.render_into(WriteFmt(output))
177    }
178
179    /// Render the output into an [`io::Write`](std::io::Write) object
180    #[cfg(feature = "std")]
181    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
182    #[inline]
183    fn render_io(&self, output: impl std::io::Write) -> fmt::Result {
184        self.render_into(WriteIo(output))
185    }
186
187    /// Render the output into a [`String`]
188    #[cfg(feature = "alloc")]
189    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
190    #[inline]
191    fn render_string(&self, output: &mut alloc::string::String) -> fmt::Result {
192        self.render_into(WriteString(output))
193    }
194}