This commit is contained in:
2025-04-22 10:39:29 +04:00
parent 15b05cb599
commit 34594b95fa
10 changed files with 208 additions and 299 deletions

View File

@@ -1,5 +1,6 @@
use actix_web::{error, Error, HttpResponse};
use chrono::{DateTime, Utc};
use tera::{self, Function, Result, Value};
use tera::{self, Context, Function, Tera, Value};
// Export modules
pub mod redis_service;
@@ -7,6 +8,22 @@ pub mod redis_service;
// Re-export for easier imports
pub use redis_service::RedisCalendarService;
/// Error type for template rendering
#[derive(Debug)]
pub struct TemplateError {
pub message: String,
pub details: String,
pub location: String,
}
impl std::fmt::Display for TemplateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Template error in {}: {}", self.location, self.message)
}
}
impl std::error::Error for TemplateError {}
/// Registers custom Tera functions
pub fn register_tera_functions(tera: &mut tera::Tera) {
tera.register_function("now", NowFunction);
@@ -18,7 +35,7 @@ pub fn register_tera_functions(tera: &mut tera::Tera) {
pub struct NowFunction;
impl Function for NowFunction {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> Result<Value> {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> tera::Result<Value> {
let format = match args.get("format") {
Some(val) => match val.as_str() {
Some(s) => s,
@@ -43,7 +60,7 @@ impl Function for NowFunction {
pub struct FormatDateFunction;
impl Function for FormatDateFunction {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> Result<Value> {
fn call(&self, args: &std::collections::HashMap<String, Value>) -> tera::Result<Value> {
let timestamp = match args.get("timestamp") {
Some(val) => match val.as_i64() {
Some(ts) => ts,
@@ -96,6 +113,50 @@ pub fn truncate_string(s: &str, max_length: usize) -> String {
}
}
/// Renders a template with error handling
///
/// This function attempts to render a template and handles any errors by rendering
/// the error template with detailed error information.
pub fn render_template(
tmpl: &Tera,
template_name: &str,
ctx: &Context,
) -> Result<HttpResponse, Error> {
match tmpl.render(template_name, ctx) {
Ok(content) => Ok(HttpResponse::Ok().content_type("text/html").body(content)),
Err(e) => {
// Log the error
log::error!("Template rendering error: {}", e);
log::error!("Error details: {:?}", e);
log::error!("Context: {:?}", ctx);
// Create a context for the error template
let mut error_ctx = Context::new();
error_ctx.insert("error", &format!("Template rendering error: {}", e));
error_ctx.insert("error_details", &format!("{:?}", e));
error_ctx.insert("error_location", &template_name);
// Try to render the error template
match tmpl.render("error.html", &error_ctx) {
Ok(error_page) => {
// Return the error page with a 500 status code
Ok(HttpResponse::InternalServerError()
.content_type("text/html")
.body(error_page))
}
Err(render_err) => {
// If we can't render the error template, log it and return a basic error
log::error!("Error rendering error template: {}", render_err);
Err(error::ErrorInternalServerError(format!(
"Template rendering error: {}. Failed to render error page: {}",
e, render_err
)))
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;