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}