use std::{net::AddrParseError, num::ParseIntError};
use config::ConfigError;
use serde::{ser::SerializeMap, Serialize};
use thiserror::Error;
use tracing::error;
use crate::{model, security};
#[derive(Error, Debug)]
pub enum DataError {
#[error("Biz error. code: {code}, msg: {msg}")]
BizError { code: i32, msg: String },
#[error(transparent)]
SecurityError(#[from] security::Error),
#[error(transparent)]
FusionCommonError(#[from] ultimate_common::Error),
#[error(transparent)]
SystemTimeError(#[from] std::time::SystemTimeError),
#[error(transparent)]
ParseIntError(#[from] ParseIntError),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
JsonError(#[from] serde_json::Error),
#[cfg(feature = "tonic")]
#[error(transparent)]
GrpcTransportError(#[from] tonic::transport::Error),
}
impl DataError {
pub fn bad_request(msg: impl Into<String>) -> Self {
DataError::BizError { code: 400, msg: msg.into() }
}
pub fn not_found(msg: impl Into<String>) -> Self {
DataError::BizError { code: 404, msg: msg.into() }
}
pub fn confilicted(msg: impl Into<String>) -> Self {
DataError::BizError { code: 409, msg: msg.into() }
}
pub fn server_error(msg: impl Into<String>) -> Self {
DataError::BizError { code: 500, msg: msg.into() }
}
pub fn unauthorized(msg: impl Into<String>) -> Self {
DataError::BizError { code: 401, msg: msg.into() }
}
pub fn forbidden(msg: impl Into<String>) -> Self {
DataError::BizError { code: 403, msg: msg.into() }
}
pub fn ok(msg: impl Into<String>) -> Self {
DataError::BizError { code: 0, msg: msg.into() }
}
}
impl From<model::Error> for DataError {
fn from(e: model::Error) -> Self {
match e {
model::Error::EntityNotFound { .. } => Self::not_found(e.to_string()),
model::Error::NotFound { .. } => Self::not_found(e.to_string()),
model::Error::UserAlreadyExists { .. } => Self::confilicted(e.to_string()),
model::Error::UniqueViolation { .. } => Self::confilicted(e.to_string()),
model::Error::SeaQuery(_) => Self::bad_request(e.to_string()),
_ => DataError::server_error(e.to_string()),
}
}
}
impl From<model::store::Error> for DataError {
fn from(e: model::store::Error) -> Self {
DataError::server_error(e.to_string())
}
}
impl From<ConfigError> for DataError {
fn from(value: ConfigError) -> Self {
DataError::server_error(format!("Config load error: {:?}", value.to_string()))
}
}
impl From<AddrParseError> for DataError {
fn from(value: AddrParseError) -> Self {
DataError::server_error(format!("Addr parse error: {}", value))
}
}
impl Serialize for DataError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("aa", "error")?;
map.end()
}
}
#[cfg(feature = "tonic")]
impl From<tonic::Status> for DataError {
fn from(value: tonic::Status) -> Self {
let msg = value.message();
match value.code() {
tonic::Code::Ok => DataError::ok(msg),
tonic::Code::Cancelled => DataError::server_error(msg),
tonic::Code::Unknown => DataError::server_error(msg),
tonic::Code::InvalidArgument => DataError::bad_request(msg),
tonic::Code::DeadlineExceeded => DataError::server_error(msg),
tonic::Code::NotFound => DataError::not_found(msg),
tonic::Code::AlreadyExists => DataError::confilicted(msg),
tonic::Code::PermissionDenied => DataError::server_error(msg),
tonic::Code::ResourceExhausted => DataError::server_error(msg),
tonic::Code::FailedPrecondition => DataError::forbidden(msg),
tonic::Code::Aborted => DataError::server_error(msg),
tonic::Code::OutOfRange => DataError::bad_request(msg),
tonic::Code::Unimplemented => DataError::server_error(msg),
tonic::Code::Internal => DataError::server_error(msg),
tonic::Code::Unavailable => DataError::server_error(msg),
tonic::Code::DataLoss => DataError::server_error(msg),
tonic::Code::Unauthenticated => DataError::unauthorized(msg),
}
}
}
#[cfg(feature = "tonic")]
impl From<DataError> for tonic::Status {
fn from(value: DataError) -> Self {
match value {
DataError::BizError { code, msg } => make_tonic_status(code, msg),
DataError::Unauthorized => tonic::Status::unauthenticated("Unauthorized"),
DataError::SecurityError(_) => tonic::Status::unauthenticated("Token error"),
DataError::DbErr(ex) => tonic::Status::from_error(ex.into()),
DataError::FusionCommonError(ex) => tonic::Status::from_error(ex.into()),
DataError::AnyhowError(ex) => tonic::Status::from_error(ex.into()),
DataError::SystemTimeError(ex) => tonic::Status::from_error(ex.into()),
DataError::ParseIntError(ex) => tonic::Status::from_error(ex.into()),
DataError::GrpcTransportError(ex) => tonic::Status::from_error(ex.into()),
DataError::IoError(e) => tonic::Status::internal(e.to_string()),
}
}
}
#[cfg(feature = "tonic")]
fn make_tonic_status(code: i32, msg: String) -> tonic::Status {
if code == 0 || (200..300).contains(&code) {
return tonic::Status::ok(msg);
}
if code == 400 {
return tonic::Status::invalid_argument(msg);
}
if code == 401 {
return tonic::Status::unauthenticated(msg);
}
if code == 403 {
return tonic::Status::permission_denied(msg);
}
if code == 404 {
return tonic::Status::not_found(msg);
}
if code == 409 {
return tonic::Status::already_exists(msg);
}
if code == 501 {
return tonic::Status::unimplemented(msg);
}
tonic::Status::internal(msg)
}