wae_https/template/
mod.rs1use http::{Response, StatusCode, header};
12use std::path::PathBuf;
13
14use crate::{Body, full_body};
15
16pub use askama;
17
18#[derive(Debug, Clone)]
20pub struct TemplateConfig {
21 pub template_dir: PathBuf,
23 pub extension: String,
25 pub enable_cache: bool,
27}
28
29impl Default for TemplateConfig {
30 fn default() -> Self {
31 Self { template_dir: PathBuf::from("templates"), extension: "html".to_string(), enable_cache: true }
32 }
33}
34
35impl TemplateConfig {
36 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn with_template_dir(mut self, dir: impl Into<PathBuf>) -> Self {
43 self.template_dir = dir.into();
44 self
45 }
46
47 pub fn with_extension(mut self, ext: impl Into<String>) -> Self {
49 self.extension = ext.into();
50 self
51 }
52
53 pub fn with_cache(mut self, enable: bool) -> Self {
55 self.enable_cache = enable;
56 self
57 }
58}
59
60#[derive(Debug, Clone)]
75pub struct TemplateRenderer {
76 config: TemplateConfig,
77}
78
79impl TemplateRenderer {
80 pub fn new(config: TemplateConfig) -> Self {
82 Self { config }
83 }
84
85 pub fn default_renderer() -> Self {
87 Self::new(TemplateConfig::default())
88 }
89
90 pub fn config(&self) -> &TemplateConfig {
92 &self.config
93 }
94
95 pub fn template_dir(&self) -> &PathBuf {
97 &self.config.template_dir
98 }
99}
100
101impl Default for TemplateRenderer {
102 fn default() -> Self {
103 Self::default_renderer()
104 }
105}
106
107#[derive(Debug)]
109pub enum TemplateError {
110 NotFound(String),
112 RenderError(String),
114 IoError(String),
116}
117
118impl std::fmt::Display for TemplateError {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 match self {
121 TemplateError::NotFound(name) => write!(f, "Template not found: {}", name),
122 TemplateError::RenderError(msg) => write!(f, "Template render error: {}", msg),
123 TemplateError::IoError(msg) => write!(f, "Template IO error: {}", msg),
124 }
125 }
126}
127
128impl std::error::Error for TemplateError {}
129
130pub struct HtmlTemplate<T> {
135 inner: T,
137}
138
139impl<T> HtmlTemplate<T> {
140 pub fn new(template: T) -> Self {
142 Self { inner: template }
143 }
144
145 pub fn inner(&self) -> &T {
147 &self.inner
148 }
149
150 pub fn into_inner(self) -> T {
152 self.inner
153 }
154}
155
156impl<T> From<T> for HtmlTemplate<T> {
157 fn from(template: T) -> Self {
158 Self::new(template)
159 }
160}
161
162impl<T> HtmlTemplate<T>
163where
164 T: askama::Template,
165{
166 pub fn into_response(self) -> Response<Body> {
168 match self.inner.render() {
169 Ok(html) => Response::builder()
170 .status(StatusCode::OK)
171 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
172 .body(full_body(html))
173 .unwrap(),
174 Err(e) => {
175 tracing::error!("Template render error: {}", e);
176 Response::builder()
177 .status(StatusCode::INTERNAL_SERVER_ERROR)
178 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
179 .body(full_body(format!("Template error: {}", e)))
180 .unwrap()
181 }
182 }
183 }
184}
185
186pub struct TemplateResponse;
190
191impl TemplateResponse {
192 pub fn html<T: askama::Template>(template: T) -> Response<Body> {
194 HtmlTemplate::new(template).into_response()
195 }
196
197 pub fn html_with_status<T: askama::Template>(template: T, status: StatusCode) -> Response<Body> {
199 match template.render() {
200 Ok(html) => Response::builder()
201 .status(status)
202 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
203 .body(full_body(html))
204 .unwrap(),
205 Err(e) => {
206 tracing::error!("Template render error: {}", e);
207 Response::builder()
208 .status(StatusCode::INTERNAL_SERVER_ERROR)
209 .header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
210 .body(full_body(format!("Template error: {}", e)))
211 .unwrap()
212 }
213 }
214 }
215}