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