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;
79use core::ops::Deref;
80#[cfg(feature = "std")]
81use std::io;
82
83#[cfg(feature = "derive")]
84pub use askama_derive::Template;
85
86#[doc(hidden)]
87pub use crate as shared;
88pub use crate::error::{Error, Result};
89pub use crate::helpers::PrimitiveType;
90pub use crate::values::{NO_VALUES, Value, Values, get_value};
91
92pub trait Template: fmt::Display + FastWritable {
114 #[inline]
116 #[cfg(feature = "alloc")]
117 fn render(&self) -> Result<String> {
118 self.render_with_values(NO_VALUES)
119 }
120
121 #[inline]
123 #[cfg(feature = "alloc")]
124 fn render_with_values(&self, values: &dyn Values) -> Result<String> {
125 let mut buf = String::new();
126 let _ = buf.try_reserve(Self::SIZE_HINT);
127 self.render_into_with_values(&mut buf, values)?;
128 Ok(buf)
129 }
130
131 #[inline]
133 fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
134 self.render_into_with_values(writer, NO_VALUES)
135 }
136
137 fn render_into_with_values<W: fmt::Write + ?Sized>(
139 &self,
140 writer: &mut W,
141 values: &dyn Values,
142 ) -> Result<()>;
143
144 #[inline]
146 #[cfg(feature = "std")]
147 fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
148 self.write_into_with_values(writer, NO_VALUES)
149 }
150
151 #[cfg(feature = "std")]
153 fn write_into_with_values<W: io::Write + ?Sized>(
154 &self,
155 writer: &mut W,
156 values: &dyn Values,
157 ) -> io::Result<()> {
158 struct Wrapped<W: io::Write> {
159 writer: W,
160 err: Option<io::Error>,
161 }
162
163 impl<W: io::Write> fmt::Write for Wrapped<W> {
164 fn write_str(&mut self, s: &str) -> fmt::Result {
165 if let Err(err) = self.writer.write_all(s.as_bytes()) {
166 self.err = Some(err);
167 Err(fmt::Error)
168 } else {
169 Ok(())
170 }
171 }
172 }
173
174 let mut wrapped = Wrapped { writer, err: None };
175 if self.render_into_with_values(&mut wrapped, values).is_ok() {
176 Ok(())
177 } else {
178 let err = wrapped.err.take();
179 Err(err.unwrap_or_else(|| io::Error::new(io::ErrorKind::Other, fmt::Error)))
180 }
181 }
182
183 const SIZE_HINT: usize;
194}
195
196impl<T: Template + ?Sized> Template for &T {
197 #[inline]
198 #[cfg(feature = "alloc")]
199 fn render(&self) -> Result<String> {
200 <T as Template>::render(self)
201 }
202
203 #[inline]
204 #[cfg(feature = "alloc")]
205 fn render_with_values(&self, values: &dyn Values) -> Result<String> {
206 <T as Template>::render_with_values(self, values)
207 }
208
209 #[inline]
210 fn render_into<W: fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()> {
211 <T as Template>::render_into(self, writer)
212 }
213
214 #[inline]
215 fn render_into_with_values<W: fmt::Write + ?Sized>(
216 &self,
217 writer: &mut W,
218 values: &dyn Values,
219 ) -> Result<()> {
220 <T as Template>::render_into_with_values(self, writer, values)
221 }
222
223 #[inline]
224 #[cfg(feature = "std")]
225 fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
226 <T as Template>::write_into(self, writer)
227 }
228
229 #[inline]
230 #[cfg(feature = "std")]
231 fn write_into_with_values<W: io::Write + ?Sized>(
232 &self,
233 writer: &mut W,
234 values: &dyn Values,
235 ) -> io::Result<()> {
236 <T as Template>::write_into_with_values(self, writer, values)
237 }
238
239 const SIZE_HINT: usize = T::SIZE_HINT;
240}
241
242pub trait DynTemplate {
248 #[cfg(feature = "alloc")]
250 fn dyn_render(&self) -> Result<String>;
251
252 #[cfg(feature = "alloc")]
254 fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String>;
255
256 fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()>;
258
259 fn dyn_render_into_with_values(
261 &self,
262 writer: &mut dyn fmt::Write,
263 values: &dyn Values,
264 ) -> Result<()>;
265
266 #[cfg(feature = "std")]
268 fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()>;
269
270 #[cfg(feature = "std")]
272 fn dyn_write_into_with_values(
273 &self,
274 writer: &mut dyn io::Write,
275 values: &dyn Values,
276 ) -> io::Result<()>;
277
278 fn size_hint(&self) -> usize;
280}
281
282impl<T: Template> DynTemplate for T {
283 #[inline]
284 #[cfg(feature = "alloc")]
285 fn dyn_render(&self) -> Result<String> {
286 <Self as Template>::render(self)
287 }
288
289 #[cfg(feature = "alloc")]
290 fn dyn_render_with_values(&self, values: &dyn Values) -> Result<String> {
291 <Self as Template>::render_with_values(self, values)
292 }
293
294 #[inline]
295 fn dyn_render_into(&self, writer: &mut dyn fmt::Write) -> Result<()> {
296 <Self as Template>::render_into(self, writer)
297 }
298
299 fn dyn_render_into_with_values(
300 &self,
301 writer: &mut dyn fmt::Write,
302 values: &dyn Values,
303 ) -> Result<()> {
304 <Self as Template>::render_into_with_values(self, writer, values)
305 }
306
307 #[cfg(feature = "std")]
308 fn dyn_write_into(&self, writer: &mut dyn io::Write) -> io::Result<()> {
309 <Self as Template>::write_into(self, writer)
310 }
311
312 #[inline]
313 #[cfg(feature = "std")]
314 fn dyn_write_into_with_values(
315 &self,
316 writer: &mut dyn io::Write,
317 values: &dyn Values,
318 ) -> io::Result<()> {
319 <Self as Template>::write_into_with_values(self, writer, values)
320 }
321
322 #[inline]
323 fn size_hint(&self) -> usize {
324 <Self as Template>::SIZE_HINT
325 }
326}
327
328impl fmt::Display for dyn DynTemplate {
329 #[inline]
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 self.dyn_render_into(f).map_err(|_| fmt::Error {})
332 }
333}
334
335macro_rules! impl_for_ref {
337 (impl $Trait:ident for $T:ident $body:tt) => {
338 const _: () = {
339 crate::impl_for_ref! {
340 impl<$T> $Trait for [
341 &T
342 &mut T
343 core::cell::Ref<'_, T>
344 core::cell::RefMut<'_, T>
345 ] $body
346 }
347 };
348 #[cfg(feature = "alloc")]
349 const _: () = {
350 crate::impl_for_ref! {
351 impl<$T> $Trait for [
352 alloc::boxed::Box<T>
353 alloc::rc::Rc<T>
354 alloc::sync::Arc<T>
355 ] $body
356 }
357 };
358 #[cfg(feature = "std")]
359 const _: () = {
360 crate::impl_for_ref! {
361 impl<$T> $Trait for [
362 std::sync::MutexGuard<'_, T>
363 std::sync::RwLockReadGuard<'_, T>
364 std::sync::RwLockWriteGuard<'_, T>
365 ] $body
366 }
367 };
368 };
369 (impl<$T:ident> $Trait:ident for [$($ty:ty)*] $body:tt) => {
370 $(impl<$T: $Trait + ?Sized> $Trait for $ty $body)*
371 }
372}
373
374pub trait FastWritable {
376 fn write_into<W: fmt::Write + ?Sized>(
378 &self,
379 dest: &mut W,
380 values: &dyn Values,
381 ) -> crate::Result<()>;
382}
383
384const _: () = {
385 crate::impl_for_ref! {
386 impl FastWritable for T {
387 #[inline]
388 fn write_into<W: fmt::Write + ?Sized>(
389 &self,
390 dest: &mut W,
391 values: &dyn Values,
392 ) -> crate::Result<()> {
393 <T>::write_into(self, dest, values)
394 }
395 }
396 }
397
398 impl<T> FastWritable for core::pin::Pin<T>
399 where
400 T: Deref,
401 <T as Deref>::Target: FastWritable,
402 {
403 #[inline]
404 fn write_into<W: fmt::Write + ?Sized>(
405 &self,
406 dest: &mut W,
407 values: &dyn Values,
408 ) -> crate::Result<()> {
409 self.as_ref().get_ref().write_into(dest, values)
410 }
411 }
412
413 #[cfg(feature = "alloc")]
414 impl<T: FastWritable + alloc::borrow::ToOwned> FastWritable for alloc::borrow::Cow<'_, T> {
415 #[inline]
416 fn write_into<W: fmt::Write + ?Sized>(
417 &self,
418 dest: &mut W,
419 values: &dyn Values,
420 ) -> crate::Result<()> {
421 T::write_into(self.as_ref(), dest, values)
422 }
423 }
424
425 macro_rules! impl_for_int {
427 ($($ty:ty)*) => { $(
428 impl FastWritable for $ty {
429 #[inline]
430 fn write_into<W: fmt::Write + ?Sized>(
431 &self,
432 dest: &mut W,
433 values: &dyn Values,
434 ) -> crate::Result<()> {
435 itoa::Buffer::new().format(*self).write_into(dest, values)
436 }
437 }
438 )* };
439 }
440
441 impl_for_int!(
442 u8 u16 u32 u64 u128 usize
443 i8 i16 i32 i64 i128 isize
444 );
445
446 macro_rules! impl_for_nz_int {
448 ($($id:ident)*) => { $(
449 impl FastWritable for core::num::$id {
450 #[inline]
451 fn write_into<W: fmt::Write + ?Sized>(
452 &self,
453 dest: &mut W,
454 values: &dyn Values,
455 ) -> crate::Result<()> {
456 self.get().write_into(dest, values)
457 }
458 }
459 )* };
460 }
461
462 impl_for_nz_int!(
463 NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
464 NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize
465 );
466
467 impl FastWritable for str {
468 #[inline]
469 fn write_into<W: fmt::Write + ?Sized>(
470 &self,
471 dest: &mut W,
472 _: &dyn Values,
473 ) -> crate::Result<()> {
474 Ok(dest.write_str(self)?)
475 }
476 }
477
478 #[cfg(feature = "alloc")]
479 impl FastWritable for alloc::string::String {
480 #[inline]
481 fn write_into<W: fmt::Write + ?Sized>(
482 &self,
483 dest: &mut W,
484 values: &dyn Values,
485 ) -> crate::Result<()> {
486 self.as_str().write_into(dest, values)
487 }
488 }
489
490 impl FastWritable for bool {
491 #[inline]
492 fn write_into<W: fmt::Write + ?Sized>(
493 &self,
494 dest: &mut W,
495 _: &dyn Values,
496 ) -> crate::Result<()> {
497 Ok(dest.write_str(match self {
498 true => "true",
499 false => "false",
500 })?)
501 }
502 }
503
504 impl FastWritable for char {
505 #[inline]
506 fn write_into<W: fmt::Write + ?Sized>(
507 &self,
508 dest: &mut W,
509 _: &dyn Values,
510 ) -> crate::Result<()> {
511 Ok(dest.write_char(*self)?)
512 }
513 }
514
515 impl FastWritable for fmt::Arguments<'_> {
516 fn write_into<W: fmt::Write + ?Sized>(
517 &self,
518 dest: &mut W,
519 _: &dyn Values,
520 ) -> crate::Result<()> {
521 Ok(match self.as_str() {
522 Some(s) => dest.write_str(s),
523 None => dest.write_fmt(*self),
524 }?)
525 }
526 }
527
528 impl<S: crate::Template + ?Sized> filters::WriteWritable for &filters::Writable<'_, S> {
529 #[inline]
530 fn askama_write<W: fmt::Write + ?Sized>(
531 &self,
532 dest: &mut W,
533 values: &dyn Values,
534 ) -> crate::Result<()> {
535 self.0.render_into_with_values(dest, values)
536 }
537 }
538
539 impl<S: FastWritable + ?Sized> filters::WriteWritable for &&filters::Writable<'_, S> {
540 #[inline]
541 fn askama_write<W: fmt::Write + ?Sized>(
542 &self,
543 dest: &mut W,
544 values: &dyn Values,
545 ) -> crate::Result<()> {
546 self.0.write_into(dest, values)
547 }
548 }
549
550 impl<S: fmt::Display + ?Sized> filters::WriteWritable for &&&filters::Writable<'_, S> {
551 #[inline]
552 fn askama_write<W: fmt::Write + ?Sized>(
553 &self,
554 dest: &mut W,
555 _: &dyn Values,
556 ) -> crate::Result<()> {
557 Ok(write!(dest, "{}", self.0)?)
558 }
559 }
560};
561
562pub(crate) use impl_for_ref;
563
564#[cfg(all(test, feature = "alloc"))]
565mod tests {
566 use std::fmt;
567
568 use super::*;
569 use crate::{DynTemplate, Template};
570
571 #[test]
572 fn dyn_template() {
573 use alloc::string::ToString;
574
575 struct Test;
576
577 impl Template for Test {
578 fn render_into_with_values<W: fmt::Write + ?Sized>(
579 &self,
580 writer: &mut W,
581 _values: &dyn Values,
582 ) -> Result<()> {
583 Ok(writer.write_str("test")?)
584 }
585
586 const SIZE_HINT: usize = 4;
587 }
588
589 impl fmt::Display for Test {
590 #[inline]
591 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592 self.render_into(f).map_err(|_| fmt::Error {})
593 }
594 }
595
596 impl FastWritable for Test {
597 #[inline]
598 fn write_into<W: fmt::Write + ?Sized>(
599 &self,
600 f: &mut W,
601 values: &dyn Values,
602 ) -> crate::Result<()> {
603 self.render_into_with_values(f, values)
604 }
605 }
606
607 fn render(t: &dyn DynTemplate) -> String {
608 t.dyn_render().unwrap()
609 }
610
611 let test = &Test as &dyn DynTemplate;
612
613 assert_eq!(render(test), "test");
614
615 assert_eq!(test.to_string(), "test");
616
617 assert_eq!(alloc::format!("{test}"), "test");
618
619 let mut vec = alloc::vec![];
620 test.dyn_write_into(&mut vec).unwrap();
621 assert_eq!(vec, alloc::vec![b't', b'e', b's', b't']);
622 }
623}