/* * Copyright (C) 2022 Aravinth Manivannan * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::fmt; use actix_web::{ error::ResponseError, http::{header::ContentType, StatusCode}, HttpResponse, HttpResponseBuilder, }; use derive_more::Display; use derive_more::Error; use serde::*; use super::TemplateFile; use crate::errors::ServiceError; pub const ERROR_KEY: &str = "error"; pub const ERROR_TEMPLATE: TemplateFile = TemplateFile::new("error_comp", "components/error.html"); pub fn register_templates(t: &mut tera::Tera) { ERROR_TEMPLATE.register(t).expect(ERROR_TEMPLATE.name); } /// Render template with error context pub trait CtxError { fn with_error(&self, e: &ReadableError) -> String; } #[derive(Serialize, Debug, Display, Clone)] #[display(fmt = "title: {} reason: {}", title, reason)] pub struct ReadableError { pub reason: String, pub title: String, } impl ReadableError { pub fn new(e: &ServiceError) -> Self { let reason = format!("{}", e); let title = format!("{}", e.status_code()); Self { reason, title } } } #[derive(Error, Display)] #[display(fmt = "{}", readable)] pub struct PageError { #[error(not(source))] template: T, readable: ReadableError, #[error(not(source))] error: ServiceError, } impl fmt::Debug for PageError { #[cfg(not(tarpaulin_include))] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PageError") .field("readable", &self.readable) .finish() } } impl PageError { /// create new instance of [PageError] from a template and an error pub fn new(template: T, error: ServiceError) -> Self { let readable = ReadableError::new(&error); Self { error, template, readable, } } } #[cfg(not(tarpaulin_include))] impl ResponseError for PageError { fn error_response(&self) -> HttpResponse { HttpResponseBuilder::new(self.status_code()) .content_type(ContentType::html()) .body(self.template.with_error(&self.readable)) } fn status_code(&self) -> StatusCode { self.error.status_code() } } /// Generic result data structure #[cfg(not(tarpaulin_include))] pub type PageResult = std::result::Result>;