1use core::fmt;
2
3#[cfg(feature = "alloc")]
4use alloc::string::String;
5
6#[cfg(feature = "std")]
7use std::io;
8
9use crate::error::{self, Error};
10use crate::render::RenderOnce;
11
12pub trait Template: RenderOnce + Sized {
17 #[cfg(feature = "alloc")]
21 fn into_string(self) -> Result<String, Error> {
22 let mut string = String::with_capacity(self.size_hint());
23 self.write_to_string(&mut string)?;
24 string.shrink_to_fit();
25 Ok(string)
26 }
27
28 #[cfg(feature = "alloc")]
34 fn write_to_string(self, string: &mut String) -> Result<(), Error> {
35 let mut buffer = TemplateBuffer {
36 writer: InnerTemplateWriter::Str(string),
37 error: Default::default(),
38 };
39 self.render_once(&mut buffer);
40 buffer.into_result()
41 }
42
43 fn write_to_fmt(self, writer: &mut dyn fmt::Write) -> Result<(), Error> {
47 let mut buffer = TemplateBuffer {
48 writer: InnerTemplateWriter::Fmt(writer),
49 error: Default::default(),
50 };
51 self.render_once(&mut buffer);
52 buffer.into_result()
53 }
54
55 #[cfg(feature = "std")]
63 fn write_to_io(self, writer: &mut dyn io::Write) -> Result<(), Error> {
64 let mut buffer = TemplateBuffer {
65 writer: InnerTemplateWriter::Io(writer),
66 error: Default::default(),
67 };
68 self.render_once(&mut buffer);
69 buffer.into_result()
70 }
71}
72
73impl<T: RenderOnce + Sized> Template for T {}
74
75pub struct TemplateBuffer<'a> {
88 writer: InnerTemplateWriter<'a>,
89 error: Error,
90}
91
92enum InnerTemplateWriter<'a> {
93 Fmt(&'a mut dyn fmt::Write),
94 #[cfg(feature = "alloc")]
95 Str(&'a mut String),
96 #[cfg(feature = "std")]
97 Io(&'a mut dyn io::Write),
98}
99
100impl<'a> TemplateBuffer<'a> {
101 #[cold]
111 #[cfg(feature = "std")]
112 pub fn record_error<E: Into<Box<dyn std::error::Error + Send + Sync>>>(&mut self, e: E) {
113 self.error.render.push(e.into());
114 }
115
116 #[cold]
117 #[cfg(all(not(feature = "std"), feature = "alloc"))]
118 pub fn record_error<E: alloc::string::ToString>(&mut self, e: E) {
119 self.error.render.push(e.to_string());
120 }
121
122 #[cold]
123 #[cfg(not(feature = "alloc"))]
124 pub fn record_error(&mut self, e: &'static str) {
125 if self.error.render.is_none() {
126 self.error.render = Some(e);
127 }
128 }
129
130 #[inline(always)]
133 pub fn write_raw(&mut self, text: &str) {
134 use alloc::fmt::Write;
135 let _ = self.as_raw_writer().write_str(text);
136 }
137
138 #[inline]
155 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) {
156 use alloc::fmt::Write;
157 let _ = self.as_writer().write_fmt(args);
158 }
159
160 #[inline]
162 pub fn write_str(&mut self, text: &str) {
163 use alloc::fmt::Write;
164 let _ = self.as_writer().write_str(text);
165 }
166
167 #[inline]
169 pub fn as_writer<'b>(&'b mut self) -> TemplateWriter<'a, 'b> {
170 TemplateWriter(self)
171 }
172
173 #[inline]
175 pub fn as_raw_writer<'b>(&'b mut self) -> RawTemplateWriter<'a, 'b> {
176 RawTemplateWriter(self)
177 }
178
179 fn into_result(self) -> Result<(), Error> {
180 if error::is_empty(&self.error) {
181 Ok(())
182 } else {
183 Err(self.error)
184 }
185 }
186}
187
188#[cfg(not(feature = "std"))]
189#[inline(always)]
190fn new_fmt_err() -> fmt::Error {
191 fmt::Error
192}
193
194#[cfg(feature = "std")]
195#[inline(always)]
196fn new_fmt_err() -> io::Error {
197 io::Error::new(io::ErrorKind::Other, "Format Error")
198}
199
200pub struct RawTemplateWriter<'a, 'b>(&'b mut TemplateBuffer<'a>);
204
205impl<'a, 'b> fmt::Write for RawTemplateWriter<'a, 'b> {
206 #[inline(always)]
209 fn write_str(&mut self, text: &str) -> fmt::Result {
210 use self::InnerTemplateWriter::*;
211 if !error::is_empty(&self.0.error) {
212 return Ok(());
213 }
214 match self.0.writer {
215 Fmt(ref mut writer) => {
216 if writer.write_str(text).is_err() {
217 self.0.error.write = Some(new_fmt_err());
218 }
219 }
220 #[cfg(feature = "alloc")]
221 Str(ref mut writer) => {
222 let _ = writer.write_str(text);
223 }
224 #[cfg(feature = "std")]
225 Io(ref mut writer) => {
226 self.0.error.write = writer.write_all(text.as_bytes()).err();
227 }
228 }
229 Ok(())
230 }
231}
232
233pub struct TemplateWriter<'a, 'b>(&'b mut TemplateBuffer<'a>);
237
238impl<'a, 'b> fmt::Write for TemplateWriter<'a, 'b> {
239 fn write_str(&mut self, text: &str) -> fmt::Result {
240 use self::InnerTemplateWriter::*;
246 if !error::is_empty(&self.0.error) {
247 return Ok(());
248 }
249
250 fn should_escape(b: u8) -> bool {
251 (b | 0x4) == b'&' || (b | 0x2) == b'>'
252 }
253
254 match self.0.writer {
255 Fmt(ref mut writer) => {
256 for c in text.chars() {
257 if (match (c.is_ascii() && should_escape(c as u8), c as u8) {
258 (true, b'&') => writer.write_str("&"),
259 (true, b'"') => writer.write_str("""),
260 (true, b'<') => writer.write_str("<"),
261 (true, b'>') => writer.write_str(">"),
262 _ => writer.write_char(c),
263 })
264 .is_err()
265 {
266 self.0.error.write = Some(new_fmt_err());
267 break;
268 }
269 }
270 }
271 #[cfg(feature = "alloc")]
272 Str(ref mut writer) => {
273 for b in text.bytes() {
274 match (should_escape(b), b) {
275 (true, b'&') => writer.push_str("&"),
276 (true, b'"') => writer.push_str("""),
277 (true, b'<') => writer.push_str("<"),
278 (true, b'>') => writer.push_str(">"),
279 _ => unsafe { writer.as_mut_vec() }.push(b),
281 }
282 }
283 }
284 #[cfg(feature = "std")]
285 Io(ref mut writer) => {
286 for b in text.bytes() {
287 if let Err(e) = match (should_escape(b), b) {
288 (true, b'&') => writer.write_all(b"&"),
289 (true, b'"') => writer.write_all(b"""),
290 (true, b'<') => writer.write_all(b"<"),
291 (true, b'>') => writer.write_all(b">"),
292 _ => writer.write_all(&[b] as &[u8]),
293 } {
294 self.0.error.write = Some(e);
295 break;
296 }
297 }
298 }
299 }
300 Ok(())
301 }
302}