1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
58#![deny(elided_lifetimes_in_paths)]
59#![deny(unreachable_pub)]
60#![deny(missing_docs)]
61#![no_std]
62
63#[cfg(feature = "alloc")]
64extern crate alloc;
65#[cfg(feature = "std")]
66extern crate std;
67
68mod ascii_str;
69mod error;
70pub mod filters;
71#[doc(hidden)]
72pub mod helpers;
73mod html;
74mod values;
75
76#[cfg(feature = "alloc")]
77use alloc::string::String;
78use core::fmt;
79#[cfg(feature = "std")]
80use std::io;
81
82#[cfg(feature = "derive")]
83pub use askama_derive::Template;
84
85#[doc(hidden)]
86pub use crate as shared;
87pub use crate::error::{Error, Result};
88pub use crate::helpers::PrimitiveType;
89pub use crate::values::{NO_VALUES, Value, Values, get_value};
90
91pub trait Template: fmt::Display + filters::FastWritable {
113 #[inline]
115 #[cfg(feature = "alloc")]
116 fn render(&self) -> Result<String> {
117 self.render_with_values(NO_VALUES)
118 }
119
120 #[inline]
122 #[cfg(feature = "alloc")]
123 fn render_with_values(&self, values: &dyn Values) -> Result<String> {
124 let mut buf = String::new();
125 let _ = buf.try_reserve(Self::SIZE_HINT);
126 self.render_into_with_values(&mut buf, values)?;
127 Ok(buf)
128 }
129
130 #[inline]
132 fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
133 self.render_into_with_values(writer, NO_VALUES)
134 }
135
136 fn render_into_with_values<W: fmt::Write + ?Sized>(
138 &self,
139 writer: &mut W,
140 values: &dyn Values,
141 ) -> Result<()>;
142
143 #[inline]
145 #[cfg(feature = "std")]
146 fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
147 self.write_into_with_values(writer, NO_VALUES)
148 }
149
150 #[cfg(feature = "std")]
152 fn write_into_with_values<W: io::Write + ?Sized>(
153 &self,
154 writer: &mut W,
155 values: &dyn Values,
156 ) -> io::Result<()> {
157 struct Wrapped<W: io::Write> {
158 writer: W,
159 err: Option<io::Error>,
160 }
161
162 impl<W: io::Write> fmt::Write for Wrapped<W> {
163 fn write_str(&mut self, s: &str) -> fmt::Result {
164 if let Err(err) = self.writer.write_all(s.as_bytes()) {
165 self.err = Some(err);
166 Err(fmt::Error)
167 } else {
168 Ok(())
169 }
170 }
171 }
172
173 let mut wrapped = Wrapped { writer, err: None };
174 if self.render_into_with_values(&mut wrapped, values).is_ok() {
175 Ok(())
176 } else {
177 let err = wrapped.err.take();
178 Err(err.unwrap_or_else(|| io::Error::new(io::ErrorKind::Other, fmt::Error)))
179 }
180 }
181
182 const SIZE_HINT: usize;
193}
194
195impl<T: Template + ?Sized> Template for &T {
196 #[inline]
197 #[cfg(feature = "alloc")]
198 fn render(&self) -> Result<String> {
199 <T as Template>::render(self)
200 }
201
202 #[inline]
203 #[cfg(feature = "alloc")]
204 fn render_with_values(&self, values: &dyn Values) -> Result<String> {
205 <T as Template>::render_with_values(self, values)
206 }
207
208 #[inline]
209 fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
210 <T as Template>::render_into(self, writer)
211 }
212
213 #[inline]
214 fn render_into_with_values<W: fmt::Write + ?Sized>(
215 &self,
216 writer: &mut W,
217 values: &dyn Values,
218 ) -> Result<()> {
219 <T as Template>::render_into_with_values(self, writer, values)
220 }
221
222 #[inline]
223 #[cfg(feature = "std")]
224 fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
225 <T as Template>::write_into(self, writer)
226 }
227
228 #[inline]
229 #[cfg(feature = "std")]
230 fn write_into_with_values<W: io::Write + ?Sized>(
231 &self,
232 writer: &mut W,
233 values: &dyn Values,
234 ) -> io::Result<()> {
235 <T as Template>::write_into_with_values(self, writer, values)
236 }
237
238 const SIZE_HINT: usize = T::SIZE_HINT;
239}
240
241pub trait DynTemplate {
247 #[cfg(feature = "alloc")]
249 fn dyn_render(&self) -> Result<String>;
250
251 #[cfg(feature = "alloc")]
253 fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String>;
254
255 fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()>;
257
258 fn dyn_render_into_with_values(
260 &self,
261 writer: &mut dyn fmt::Write,
262 values: &dyn Values,
263 ) -> Result<()>;
264
265 #[cfg(feature = "std")]
267 fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()>;
268
269 #[cfg(feature = "std")]
271 fn dyn_write_into_with_values(
272 &self,
273 writer: &mut dyn io::Write,
274 values: &dyn Values,
275 ) -> io::Result<()>;
276
277 fn size_hint(&self) -> usize;
279}
280
281impl<T: Template> DynTemplate for T {
282 #[inline]
283 #[cfg(feature = "alloc")]
284 fn dyn_render(&self) -> Result<String> {
285 <Self as Template>::render(self)
286 }
287
288 #[cfg(feature = "alloc")]
289 fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String> {
290 <Self as Template>::render_with_values(self, values)
291 }
292
293 #[inline]
294 fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()> {
295 <Self as Template>::render_into(self, writer)
296 }
297
298 fn dyn_render_into_with_values(
299 &self,
300 writer: &mut dyn fmt::Write,
301 values: &dyn Values,
302 ) -> Result<()> {
303 <Self as Template>::render_into_with_values(self, writer, values)
304 }
305
306 #[cfg(feature = "std")]
307 fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()> {
308 <Self as Template>::write_into(self, writer)
309 }
310
311 #[inline]
312 #[cfg(feature = "std")]
313 fn dyn_write_into_with_values(
314 &self,
315 writer: &mut dyn io::Write,
316 values: &dyn Values,
317 ) -> io::Result<()> {
318 <Self as Template>::write_into_with_values(self, writer, values)
319 }
320
321 #[inline]
322 fn size_hint(&self) -> usize {
323 <Self as Template>::SIZE_HINT
324 }
325}
326
327impl fmt::Display for dyn DynTemplate {
328 #[inline]
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 self.dyn_render_into(f).map_err(|_| fmt::Error {})
331 }
332}
333
334macro_rules! impl_for_ref {
336 (impl $Trait:ident for $T:ident $body:tt) => {
337 const _: () = {
338 crate::impl_for_ref! {
339 impl<$T> $Trait for [
340 &T
341 &mut T
342 core::cell::Ref<'_, T>
343 core::cell::RefMut<'_, T>
344 ] $body
345 }
346 };
347 #[cfg(feature = "alloc")]
348 const _: () = {
349 crate::impl_for_ref! {
350 impl<$T> $Trait for [
351 alloc::boxed::Box<T>
352 alloc::rc::Rc<T>
353 alloc::sync::Arc<T>
354 ] $body
355 }
356 };
357 #[cfg(feature = "std")]
358 const _: () = {
359 crate::impl_for_ref! {
360 impl<$T> $Trait for [
361 std::sync::MutexGuard<'_, T>
362 std::sync::RwLockReadGuard<'_, T>
363 std::sync::RwLockWriteGuard<'_, T>
364 ] $body
365 }
366 };
367 };
368 (impl<$T:ident> $Trait:ident for [$($ty:ty)*] $body:tt) => {
369 $(impl<$T: $Trait + ?Sized> $Trait for $ty $body)*
370 }
371}
372
373pub(crate) use impl_for_ref;
374
375#[cfg(all(test, feature = "alloc"))]
376mod tests {
377 use std::fmt;
378
379 use super::*;
380 use crate::{DynTemplate, Template};
381
382 #[test]
383 fn dyn_template() {
384 use alloc::string::ToString;
385
386 struct Test;
387
388 impl Template for Test {
389 fn render_into_with_values<W: fmt::Write + ?Sized>(
390 &self,
391 writer: &mut W,
392 _values: &dyn Values,
393 ) -> Result<()> {
394 Ok(writer.write_str("test")?)
395 }
396
397 const SIZE_HINT: usize = 4;
398 }
399
400 impl fmt::Display for Test {
401 #[inline]
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 self.render_into(f).map_err(|_| fmt::Error {})
404 }
405 }
406
407 impl filters::FastWritable for Test {
408 #[inline]
409 fn write_into<W: fmt::Write + ?Sized>(&self, f: &mut W) -> crate::Result<()> {
410 self.render_into(f)
411 }
412 }
413
414 fn render(t: &dyn DynTemplate) -> String {
415 t.dyn_render().unwrap()
416 }
417
418 let test = &Test as &dyn DynTemplate;
419
420 assert_eq!(render(test), "test");
421
422 assert_eq!(test.to_string(), "test");
423
424 assert_eq!(alloc::format!("{test}"), "test");
425
426 let mut vec = alloc::vec![];
427 test.dyn_write_into(&mut vec).unwrap();
428 assert_eq!(vec, alloc::vec![b't', b'e', b's', b't']);
429 }
430}