wae_https/template/
mod.rs1use axum::{
13 body::Body,
14 http::{Response, StatusCode, header},
15 response::IntoResponse,
16};
17use std::path::PathBuf;
18
19pub use askama;
20
21#[derive(Debug, Clone)]
23pub struct TemplateConfig {
24 pub template_dir: PathBuf,
26 pub extension: String,
28 pub enable_cache: bool,
30}
31
32impl Default for TemplateConfig {
33 fn default() -> Self {
34 Self { template_dir: PathBuf::from("templates"), extension: "html".to_string(), enable_cache: true }
35 }
36}
37
38impl TemplateConfig {
39 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn with_template_dir(mut self, dir: impl Into<PathBuf>) -> Self {
46 self.template_dir = dir.into();
47 self
48 }
49
50 pub fn with_extension(mut self, ext: impl Into<String>) -> Self {
52 self.extension = ext.into();
53 self
54 }
55
56 pub fn with_cache(mut self, enable: bool) -> Self {
58 self.enable_cache = enable;
59 self
60 }
61}
62
63#[derive(Debug, Clone)]
78pub struct TemplateRenderer {
79 config: TemplateConfig,
80}
81
82impl TemplateRenderer {
83 pub fn new(config: TemplateConfig) -> Self {
85 Self { config }
86 }
87
88 pub fn default_renderer() -> Self {
90 Self::new(TemplateConfig::default())
91 }
92
93 pub fn config(&self) -> &TemplateConfig {
95 &self.config
96 }
97
98 pub fn template_dir(&self) -> &PathBuf {
100 &self.config.template_dir
101 }
102}
103
104impl Default for TemplateRenderer {
105 fn default() -> Self {
106 Self::default_renderer()
107 }
108}
109
110#[derive(Debug)]
112pub enum TemplateError {
113 NotFound(String),
115 RenderError(String),
117 IoError(String),
119}
120
121impl std::fmt::Display for TemplateError {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 TemplateError::NotFound(name) => write!(f, "Template not found: {}", name),
125 TemplateError::RenderError(msg) => write!(f, "Template render error: {}", msg),
126 TemplateError::IoError(msg) => write!(f, "Template IO error: {}", msg),
127 }
128 }
129}
130
131impl std::error::Error for TemplateError {}
132
133pub struct HtmlTemplate<T> {
138 inner: T,
140}
141
142impl<T> HtmlTemplate<T> {
143 pub fn new(template: T) -> Self {
145 Self { inner: template }
146 }
147
148 pub fn inner(&self) -> &T {
150 &self.inner
151 }
152
153 pub fn into_inner(self) -> T {
155 self.inner
156 }
157}
158
159impl<T> From<T> for HtmlTemplate<T> {
160 fn from(template: T) -> Self {
161 Self::new(template)
162 }
163}
164
165impl<T> IntoResponse for HtmlTemplate<T>
166where
167 T: askama::Template,
168{
169 fn into_response(self) -> Response<Body> {
170 match self.inner.render() {
171 Ok(html) => Response::builder()
172 .status(StatusCode::OK)
173 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
174 .body(Body::from(html))
175 .unwrap(),
176 Err(e) => {
177 tracing::error!("Template render error: {}", e);
178 Response::builder()
179 .status(StatusCode::INTERNAL_SERVER_ERROR)
180 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
181 .body(Body::from(format!("Template error: {}", e)))
182 .unwrap()
183 }
184 }
185 }
186}
187
188pub struct TemplateResponse;
192
193impl TemplateResponse {
194 pub fn html<T: askama::Template>(template: T) -> Response<Body> {
196 HtmlTemplate::new(template).into_response()
197 }
198
199 pub fn html_with_status<T: askama::Template>(template: T, status: StatusCode) -> Response<Body> {
201 match template.render() {
202 Ok(html) => Response::builder()
203 .status(status)
204 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
205 .body(Body::from(html))
206 .unwrap(),
207 Err(e) => {
208 tracing::error!("Template render error: {}", e);
209 Response::builder()
210 .status(StatusCode::INTERNAL_SERVER_ERROR)
211 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
212 .body(Body::from(format!("Template error: {}", e)))
213 .unwrap()
214 }
215 }
216 }
217}