use http::{Response, StatusCode, header};
use std::path::PathBuf;
use crate::{Body, full_body};
pub use askama;
#[derive(Debug, Clone)]
pub struct TemplateConfig {
pub template_dir: PathBuf,
pub extension: String,
pub enable_cache: bool,
}
impl Default for TemplateConfig {
fn default() -> Self {
Self { template_dir: PathBuf::from("templates"), extension: "html".to_string(), enable_cache: true }
}
}
impl TemplateConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_template_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.template_dir = dir.into();
self
}
pub fn with_extension(mut self, ext: impl Into<String>) -> Self {
self.extension = ext.into();
self
}
pub fn with_cache(mut self, enable: bool) -> Self {
self.enable_cache = enable;
self
}
}
#[derive(Debug, Clone)]
pub struct TemplateRenderer {
config: TemplateConfig,
}
impl TemplateRenderer {
pub fn new(config: TemplateConfig) -> Self {
Self { config }
}
pub fn default_renderer() -> Self {
Self::new(TemplateConfig::default())
}
pub fn config(&self) -> &TemplateConfig {
&self.config
}
pub fn template_dir(&self) -> &PathBuf {
&self.config.template_dir
}
}
impl Default for TemplateRenderer {
fn default() -> Self {
Self::default_renderer()
}
}
#[derive(Debug)]
pub enum TemplateError {
NotFound(String),
RenderError(String),
IoError(String),
}
impl std::fmt::Display for TemplateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TemplateError::NotFound(name) => write!(f, "Template not found: {}", name),
TemplateError::RenderError(msg) => write!(f, "Template render error: {}", msg),
TemplateError::IoError(msg) => write!(f, "Template IO error: {}", msg),
}
}
}
impl std::error::Error for TemplateError {}
pub struct HtmlTemplate<T> {
inner: T,
}
impl<T> HtmlTemplate<T> {
pub fn new(template: T) -> Self {
Self { inner: template }
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T> From<T> for HtmlTemplate<T> {
fn from(template: T) -> Self {
Self::new(template)
}
}
impl<T> HtmlTemplate<T>
where
T: askama::Template,
{
pub fn into_response(self) -> Response<Body> {
match self.inner.render() {
Ok(html) => Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(full_body(html))
.unwrap(),
Err(e) => {
tracing::error!("Template render error: {}", e);
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
.body(full_body(format!("Template error: {}", e)))
.unwrap()
}
}
}
}
pub struct TemplateResponse;
impl TemplateResponse {
pub fn html<T: askama::Template>(template: T) -> Response<Body> {
HtmlTemplate::new(template).into_response()
}
pub fn html_with_status<T: askama::Template>(template: T, status: StatusCode) -> Response<Body> {
match template.render() {
Ok(html) => Response::builder()
.status(status)
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
.body(full_body(html))
.unwrap(),
Err(e) => {
tracing::error!("Template render error: {}", e);
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.header(header::CONTENT_TYPE, "text/plain; charset=utf-8")
.body(full_body(format!("Template error: {}", e)))
.unwrap()
}
}
}
}