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}