// // result.rs // Copyright (C) 2022 veypi // 2022-06-24 18:57 // Distributed under terms of the Apache license. // use actix_web::ResponseError; use actix_web::{ error, http::{header::ContentType, StatusCode}, HttpResponse, }; use serde::{Deserialize, Serialize}; use thiserror::Error as ThisError; use tracing::warn; pub type Result = std::result::Result; #[derive(Serialize, Deserialize)] pub struct JsonResponse { pub content: T, } impl From for JsonResponse { fn from(e: T) -> Self { Self { content: e } } } impl actix_web::Responder for JsonResponse where T: serde::Serialize, { type Body = actix_web::body::BoxBody; fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse { HttpResponse::build(StatusCode::OK) .insert_header(ContentType::json()) .body(serde_json::to_string(&self.content).unwrap()) } } // pub type AsyncResult = std::result::Result>; #[derive(ThisError, Debug)] pub enum Error { // system // EnvVarError, #[error("Parsing listening address failed")] ParseListeningAddressFailed, #[error("Data save failed")] SledSaveFailed, #[error("Database(1) error")] SledDbError, #[error("Database(2) error")] SqliteDbError, #[error("Deserialize / Serialize failed")] SerdeError, #[error("register disabled")] AppDisabledRegister, #[error("missing {0}")] Missing(String), #[error("invalid arg {0}")] ArgInvalid(String), #[error("duplicated arg {0}")] ArgDuplicated(String), #[error("not found {0}")] NotFound(String), #[error("timeout")] Timeout, #[error("bad request")] BadRequest, #[error("Method not allowed")] MethodNotAllowed, #[error("Internal server error")] InternalServerError, // business #[error("invalid 的 Session ID")] InvalidSessionId, #[error("invalid verify code")] InvalidVerifyCode, #[error("invalid token")] InvalidToken, #[error("expired token")] ExpiredToken, #[error("no access")] NotAuthed, #[error("login failed")] LoginFailed, #[error("Registration failed")] RegisterFailed, #[error("Already registered")] AlreadyRegistered, #[error("Saving post failed")] SavePostFailed, #[error("Can not find post you requested")] CannotFoundPost, #[error("Can not find tag you requested")] CannotFoundTag, #[error("Upload failed")] UploadFailed, #[error("Upload file not found")] FileNotFound, #[error("Unknown file type")] UnknownFileType, #[error("Unsupported file type {0}")] UnsupportedFileType(String), #[error("Creating thumbnail failed")] CreateThumbnailFailed, #[error("Reading post id data by tag failed")] ReadPostIdDataByTagFailed, #[error("Saving post id data by tag failed")] SavePostIdDataByTagFailed, #[error("Tag not found")] TagNotFound, #[error("{0}")] BusinessException(String), #[error("invalid header (expected {expected:?}, found {found:?})")] InvalidHeader { expected: String, found: String }, // #[error("serde yaml error")] // SerdeYamlError{#[from] serde_yaml::Error} #[error("serde yaml error")] SerdeYamlError(#[from] serde_yaml::Error), #[error("unknown error")] Unknown, } impl From for Error { fn from(e: std::io::Error) -> Self { Error::UnsupportedFileType(format!("{:?}", e)) } } impl From for Error { fn from(e: actix_web::Error) -> Self { Error::BusinessException(format!("{:?}", e)) } } impl From for Error { fn from(e: sqlx::Error) -> Self { Error::BusinessException(format!("{:?}", e)) } } impl From for Error { fn from(e: jsonwebtoken::errors::Error) -> Self { Error::BusinessException(format!("{:?}", e)) } } impl From for Error { fn from(e: sea_orm::DbErr) -> Self { Error::BusinessException(format!("{:?}", e)) } } impl From for Error { fn from(e: aes_gcm::Error) -> Self { Error::BusinessException(format!("{:?}", e)) } } impl From for Error { fn from(e: actix_multipart::MultipartError) -> Self { Error::BusinessException(format!("{:?}", e)) } } impl From> for Error { fn from(e: Box) -> Self { Error::BusinessException(format!("{}", e)) } } impl actix_web::Responder for Error { type Body = actix_web::body::BoxBody; fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse { self.error_response() } } impl error::ResponseError for Error { fn error_response(&self) -> HttpResponse { warn!("{}", self); HttpResponse::build(self.status_code()) .insert_header(ContentType::html()) .insert_header(("error", self.to_string())) .body(self.to_string()) } fn status_code(&self) -> StatusCode { match *self { Error::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, Error::BadRequest => StatusCode::BAD_REQUEST, Error::Timeout => StatusCode::GATEWAY_TIMEOUT, Error::NotFound(_) => StatusCode::NOT_FOUND, Error::NotAuthed => StatusCode::UNAUTHORIZED, _ => StatusCode::BAD_REQUEST, } } }