askama_derive_axum/lib.rs
1//! # Status
2//!
3//! This crate is no longer supported. For a maintained alternative, use
4//! [`askama_web`](https://crates.io/crates/askama_web) for Askama + Axum integration.
5//!
6//! Derive macro for implementing [`IntoResponse`] for Askama templates.
7//!
8//! This crate provides a derive macro for implementing [`IntoResponse`] for Askama templates. This
9//! allows you to use Askama templates as responses in Axum applications. It is a replacement for
10//! the `askama_axum` crate, which will be no longer available in askama 0.13. See [askama#1128] and
11//! [askama#1119] for more information.
12//!
13//! [askama#1128]: https://github.com/rinja-rs/askama/issues/1128
14//! [askama#1119]: https://github.com/rinja-rs/askama/issues/1119
15//! [`IntoResponse`]: axum_core::response::IntoResponse
16//!
17//! # Example
18//!
19//! ```rust
20//! use askama::Template;
21//! use askama_derive_axum::IntoResponse;
22//!
23//! #[derive(Template, IntoResponse)]
24//! #[template(path = "index.html")]
25//! struct IndexTemplate {
26//! title: String,
27//! body: String,
28//! }
29//!
30//! async fn index() -> IndexTemplate {
31//! IndexTemplate {
32//! title: "My Blog".to_string(),
33//! body: "Hello, world!".to_string(),
34//! }
35//! }
36//! ```
37
38use proc_macro::TokenStream;
39use quote::quote;
40use syn::DeriveInput;
41
42/// Derive macro for implementing [`IntoResponse`] for Askama templates.
43///
44/// This macro implements the [`IntoResponse`] trait for Askama templates. It allows you to use
45/// Askama templates as responses in Axum applications. The generated implementation will render the
46/// template and return a response with the rendered body. If rendering fails, it will return a
47/// response with status code 500 (Internal Server Error).
48///
49/// # Example
50///
51/// ```rust
52/// use askama::Template;
53/// use askama_derive_axum::IntoResponse;
54///
55/// #[derive(Template, IntoResponse)]
56/// #[template(path = "index.html")]
57/// struct IndexTemplate {
58/// title: String,
59/// body: String,
60/// }
61///
62/// async fn index() -> IndexTemplate {
63/// IndexTemplate {
64/// title: "My Blog".to_string(),
65/// body: "Hello, world!".to_string(),
66/// }
67/// }
68/// ```
69///
70/// [`IntoResponse`]: axum_core::response::IntoResponse
71#[proc_macro_derive(IntoResponse)]
72pub fn into_response_derive(input: TokenStream) -> TokenStream {
73 let ast = syn::parse(input).unwrap();
74 impl_into_response(&ast)
75}
76
77fn impl_into_response(ast: &DeriveInput) -> TokenStream {
78 let name = &ast.ident;
79
80 let tracing = if cfg!(feature = "tracing") {
81 Some(quote! {
82 ::tracing::error!("Failed to render template: {e}");
83 })
84 } else {
85 None
86 };
87 let gen = quote! {
88 impl ::axum_core::response::IntoResponse for #name {
89 fn into_response(self) -> ::axum_core::response::Response {
90 use ::askama::Template;
91 use ::axum_core::{
92 body::Body,
93 response::{IntoResponse, Response},
94 };
95 use ::http::{header::{CONTENT_TYPE, HeaderValue}, StatusCode};
96
97 match self.render() {
98 Ok(body) => {
99 let headers = [(CONTENT_TYPE, HeaderValue::from_static("text/html"))];
100 (headers, body).into_response()
101 }
102 Err(e) => {
103 #tracing
104 StatusCode::INTERNAL_SERVER_ERROR.into_response()
105 }
106 }
107 }
108 }
109 };
110 gen.into()
111}