use actix_web::http::StatusCode;
use actix_web::{HttpResponse, ResponseError};
use log::error;
use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::Error as SqlxError;
use std::collections::HashMap;
use validator::{ValidationError, ValidationErrors};
#[derive(Debug, Serialize)]
pub struct InternalErrorPayload {
pub error: &'static str,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ValidationErrorPayload {
pub error: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub field_errors: Option<HashMap<String, Vec<ValidationError>>>,
}
impl ValidationErrorPayload {
pub fn new(detail: String) -> Self {
ValidationErrorPayload {
error: detail,
field_errors: None,
}
}
}
impl From<&ValidationErrors> for ValidationErrorPayload {
fn from(error: &ValidationErrors) -> Self {
let mut errors: HashMap<String, Vec<ValidationError>> = HashMap::new();
errors.extend(
error
.field_errors()
.iter()
.map(|(k, v)| (String::from(*k), (*v).clone())),
);
ValidationErrorPayload {
error: "Validation error".to_owned(),
field_errors: Some(errors),
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum AppError {
#[error("{0}")]
StaticValidation(&'static str),
#[error("{0}")]
Validation(String),
#[cfg(feature = "sqlx")]
#[error(transparent)]
DB(#[from] SqlxError),
#[error(transparent)]
Unexpected(#[from] anyhow::Error),
}
impl ResponseError for AppError {
fn status_code(&self) -> StatusCode {
match self {
Self::StaticValidation(_) | Self::Validation(_) => StatusCode::BAD_REQUEST,
Self::Unexpected(_) => StatusCode::INTERNAL_SERVER_ERROR,
#[cfg(feature = "sqlx")]
Self::DB(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let status_code = self.status_code();
match self {
Self::Validation(error) => {
HttpResponse::build(status_code)
.json(ValidationErrorPayload::new(error.to_owned()))
}
Self::StaticValidation(error) => {
HttpResponse::build(status_code)
.json(InternalErrorPayload { error })
}
_ => {
HttpResponse::build(status_code)
.json(InternalErrorPayload {
error: status_code.canonical_reason().unwrap_or("Unknown error")
})
}
}
}
}
pub type Result<T> = core::result::Result<T, AppError>;
pub type HttpResult = Result<HttpResponse>;