use actix_web::{HttpResponse, ResponseError}; use serde::Serialize; use service::error::AppError; use utoipa::openapi::schema::{KnownFormat, ObjectBuilder, SchemaFormat, Type}; use utoipa::openapi::{RefOr, Schema}; use utoipa::{PartialSchema, ToSchema}; #[derive(Debug, Serialize, ToSchema)] pub struct ApiResponse { pub code: i32, pub message: String, #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, } impl ApiResponse { pub fn ok(data: T) -> Self { Self { code: 0, message: "ok".to_string(), data: Some(data), } } pub fn to_response(self) -> HttpResponse { HttpResponse::Ok().json(self) } } pub fn api_success() -> HttpResponse { HttpResponse::Ok().json(ApiResponse { code: 0, message: "ok".to_string(), data: None::<()>, }) } #[derive(Debug, Serialize, ToSchema)] pub struct ApiErrorResponse { pub code: i32, pub error: String, pub message: String, } #[derive(Debug)] pub struct ApiError(pub AppError); impl std::fmt::Display for ApiError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.user_message().fmt(f) } } impl std::error::Error for ApiError {} impl From for ApiError { fn from(e: AppError) -> Self { ApiError(e) } } impl From for ApiError { fn from(e: room::RoomError) -> Self { ApiError(e.into()) } } impl ResponseError for ApiError { fn error_response(&self) -> HttpResponse { let err = &self.0; let status = actix_web::http::StatusCode::from_u16(err.http_status_code()) .unwrap_or(actix_web::http::StatusCode::INTERNAL_SERVER_ERROR); let resp = ApiErrorResponse { code: err.code(), error: err.slug().to_string(), message: err.user_message(), }; HttpResponse::build(status).json(resp) } } impl PartialSchema for ApiError { fn schema() -> RefOr { RefOr::T(Schema::Object( ObjectBuilder::new() .property( "code", ObjectBuilder::new() .schema_type(Type::Integer) .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int32))) .description(Some("Error numeric code")), ) .property( "error", ObjectBuilder::new() .schema_type(Type::String) .description(Some("Error slug identifier")), ) .property( "message", ObjectBuilder::new() .schema_type(Type::String) .description(Some("Human-readable error message")), ) .required("code") .required("error") .required("message") .into(), )) } } impl ToSchema for ApiError {}