use std::{error, fmt};
use std::{
fmt::{Debug, Display, Formatter},
sync::Mutex,
};
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};
use crate::tina::data::app_error::convert::IntoAppError;
use crate::tina::data::http_status::HttpStatus;
use crate::tina::data::i18n_string::I18nString;
use crate::tina::data::return_code::{IReturnCode, IntoDynReturnCode};
use crate::tina::i18n::ResourceBundle;
use anyhow::anyhow;
use either::Either;
use once_cell::sync::Lazy;
use std::any::{type_name, Any};
use std::backtrace::Backtrace;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use self::message::AppErrorMessage;
static ENABLE_BACKTRACE: Lazy<AtomicBool> = Lazy::new(|| {
let val = std::env::var("RUST_BACKTRACE").unwrap_or_default();
let flag = !matches!(val.as_str().trim(), "" | "0");
AtomicBool::new(flag)
});
pub struct AppError {
return_code: Arc<dyn IReturnCode>,
return_msg: I18nString,
detail: anyhow::Error,
location: String,
line: u32,
hierarchy: usize,
backtrace: Option<Backtrace>,
prost_message: Mutex<Option<AppErrorMessage>>,
}
impl AppError {
pub fn get_return_code(&self) -> Arc<dyn IReturnCode> {
self.return_code.clone()
}
pub fn get_return_msg(&self) -> &I18nString {
&self.return_msg
}
pub fn get_log_msg(&self) -> String {
format!("{}", self.detail)
}
pub fn get_location(&self) -> &str {
&self.location
}
pub fn get_line(&self) -> u32 {
self.line
}
pub fn new(return_code: impl IntoDynReturnCode, return_msg: I18nString, detail: anyhow::Error, location: &str, line: u32) -> AppError {
AppError {
return_code: return_code.into_dyn_return_code(),
return_msg,
detail,
location: location.to_string(),
line,
hierarchy: 0,
backtrace: match Self::is_enable_backtrace() {
true => Some(Backtrace::force_capture()),
false => None,
},
prost_message: Mutex::new(None),
}
}
pub fn from_app_err(err: &AppError, location: &str, line: u32) -> AppError {
let hierarchy = err.hierarchy + 1;
AppError {
return_code: err.return_code.clone(),
return_msg: err.return_msg.clone(),
detail: anyhow!("{:?}", err),
location: location.to_string(),
line,
hierarchy,
backtrace: match hierarchy == 0 {
true => Some(Backtrace::force_capture()),
false => None,
},
prost_message: Mutex::new(None),
}
}
pub fn from_err_custom(
err: impl error::Error + Send + Sync + 'static,
return_code: Arc<dyn IReturnCode>,
location: &str,
line: u32,
) -> AppError {
let err = anyhow!("{:?}", err);
let return_msg = return_code.get_code_description();
let mut err = Self::new(return_code, return_msg, err, location, line);
err.hierarchy += 1;
err
}
pub fn from_err_custom_msg(
err: impl error::Error + Send + Sync + 'static,
return_code: Arc<dyn IReturnCode>,
return_msg: I18nString,
location: &str,
line: u32,
) -> AppError {
let err = anyhow!("{:?}", err);
let mut err = Self::new(return_code, return_msg, err, location, line);
err.hierarchy += 1;
err
}
pub fn new_system_error(detail: anyhow::Error, location: &str, line: u32) -> AppError {
AppError::new(Arc::new(HttpStatus::Error) as Arc<dyn IReturnCode>, HttpStatus::Error.get_code_description(), detail, location, line)
}
pub fn new_error(code: impl IntoDynReturnCode, msg: I18nString, location: &str, line: u32) -> AppError {
let log_msg = msg.get_string(ResourceBundle::get_default_locale().as_str());
AppError::new(code.into_dyn_return_code(), msg, anyhow!(log_msg), location, line)
}
pub fn new_error_with_detail(
code: impl IntoDynReturnCode,
msg: I18nString,
detail: anyhow::Error,
location: &str,
line: u32,
) -> AppError {
AppError::new(code.into_dyn_return_code(), msg, detail, location, line)
}
pub fn new_param_check_error(msg: &str, location: &str, line: u32) -> AppError {
AppError::new(
Arc::new(HttpStatus::BadRequest) as Arc<dyn IReturnCode>,
HttpStatus::BadRequest.get_code_description(),
anyhow!("{}", msg),
location,
line,
)
}
pub fn new_param_check_error_with_detail(msg: I18nString, detail: anyhow::Error, location: &str, line: u32) -> AppError {
AppError::new(Arc::new(HttpStatus::BadRequest) as Arc<dyn IReturnCode>, msg, detail, location, line)
}
pub fn from_err(err: impl std::error::Error + Send + Sync + 'static, location: &str, line: u32) -> AppError {
match convert(err, location, line) {
Either::Left(err) => err,
Either::Right(either) => match either {
Either::Left(err) => {
tracing::error!("{err:?}");
let mut err = Self::new_system_error(err, location, line);
err.hierarchy += 1;
err
}
Either::Right(err) => {
tracing::error!("{err:?}");
let mut err = Self::new_system_error(anyhow!("{:?}", err), location, line);
err.hierarchy += 1;
err
}
},
}
}
pub fn from_any_send(err: Box<dyn Any + Send>) -> Either<AppError, Box<dyn Any + Send>> {
match err.downcast::<AppError>() {
Ok(err) => Either::Left(*err),
Err(err) => match err.downcast::<anyhow::Error>() {
Ok(err) => {
tracing::error!("{err:?}");
let mut err = Self::new_system_error(*err, file!(), line!());
err.hierarchy += 1;
Either::Left(err)
}
Err(err) => Either::Right(err),
},
}
}
pub fn from_none_static_err<Err: std::error::Error>(err: Err, location: &str, line: u32) -> AppError {
tracing::error!("{err:?}");
let msg = format!("{:?}", err);
let mut err = Self::new_system_error(anyhow!(msg), location, line);
err.hierarchy += 1;
err
}
pub fn from_none_send_err<Err: std::error::Error + 'static>(err: Err, location: &str, line: u32) -> AppError {
match convert(err, location, line) {
Either::Left(err) => err,
Either::Right(either) => match either {
Either::Left(err) => {
tracing::error!("{err:?}");
let mut err = Self::new_system_error(err, location, line);
err.hierarchy += 1;
err
}
Either::Right(err) => {
tracing::error!("{err:?}");
let err = anyhow!("{:?}", err);
let mut err = Self::new_system_error(err, location, line);
err.hierarchy += 1;
err
}
},
}
}
pub fn is_enable_backtrace() -> bool {
ENABLE_BACKTRACE.load(Ordering::Relaxed)
}
pub fn enable_backtrace(enable: bool) -> bool {
ENABLE_BACKTRACE.store(enable, Ordering::Relaxed);
Self::is_enable_backtrace()
}
}
fn convert<T: IntoAppError + 'static>(source: T, location: &str, line: u32) -> Either<AppError, Either<anyhow::Error, T>> {
let source = Box::new(source) as Box<dyn Any>;
match source.downcast::<AppError>() {
Ok(err) => Either::Left(AppError::from_app_err(&err, location, line)),
Err(source) => match source.downcast::<anyhow::Error>() {
Ok(err) => Either::Right(Either::Left(*err)),
Err(source) => {
let err: Box<T> = source.downcast::<T>().unwrap_or_else(|_| panic!("convert type failed: {}", type_name::<T>()));
match err.try_into_app_error(location, line) {
Either::Left(err) => Either::Left(err),
Either::Right(err) => Either::Right(Either::Right(err)),
}
}
},
}
}
impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.detail.source()
}
}
impl Debug for AppError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.hierarchy == 0 {
true => match self.backtrace.as_ref() {
Some(backtrace) => f.write_fmt(format_args!(
"return_code: {}, return_msg: {}, location: {} - {}, detail: {}{}",
self.return_code.get_code(),
self.return_msg.get_string(ResourceBundle::get_default_locale().as_str()),
self.location,
self.line,
self.detail,
format_args!("\r\nbacktrace:\r\n{}", backtrace)
)),
None => f.write_fmt(format_args!(
"return_code: {}, return_msg: {}, location: {} - {}, detail: {}",
self.return_code.get_code(),
self.return_msg.get_string(ResourceBundle::get_default_locale().as_str()),
self.location,
self.line,
self.detail,
)),
},
false => f.write_fmt(format_args!(
"return_code: {}, return_msg: {}, location: {} - {}, \r\ncause by: \r\n\t{}",
self.return_code.get_code(),
self.return_msg.get_string(ResourceBundle::get_default_locale().as_str()),
self.location,
self.line,
self.detail,
)),
}
}
}
impl Display for AppError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.get_return_msg().get_string(ResourceBundle::get_default_locale().as_str()).as_str())
}
}
impl Serialize for AppError {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("AppError", 4)?;
let return_code = &self.return_code;
s.serialize_field("return_code", &return_code.get_code())?;
let return_msg = &self.return_msg;
let return_msg_value = return_msg.get_string(ResourceBundle::get_default_locale().as_str());
s.serialize_field("return_msg", &return_msg_value)?;
s.serialize_field("detail", &self.detail.to_string())?;
s.serialize_field("location", &self.location)?;
s.serialize_field("line", &self.line)?;
s.end()
}
}
mod de {
use std::sync::Mutex;
use crate::tina::data::{app_error::AppError, http_status::HttpStatus, i18n_string::I18nString, return_code::IReturnCode};
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
use serde as _serde;
#[allow(unused_macros)]
macro_rules! __try__ {
($__expr:expr) => {
match$__expr {
_serde::__private::Ok(__val) => __val,
_serde::__private::Err(__err) => {
return _serde::__private::Err(__err);
}
}
}
}
#[automatically_derived]
impl<'de> serde::Deserialize<'de> for AppError {
fn deserialize<__D>(__deserializer: __D) -> serde::__private::Result<Self, __D::Error>
where
__D: serde::Deserializer<'de>,
{
#[allow(non_camel_case_types)]
#[doc(hidden)]
enum __Field {
__field0,
__field1,
__field2,
__field3,
__field4,
__ignore,
}
#[doc(hidden)]
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result {
_serde::__private::Formatter::write_str(__formatter, "field identifier")
}
fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
0u64 => _serde::__private::Ok(__Field::__field0),
1u64 => _serde::__private::Ok(__Field::__field1),
2u64 => _serde::__private::Ok(__Field::__field2),
3u64 => _serde::__private::Ok(__Field::__field3),
4u64 => _serde::__private::Ok(__Field::__field4),
_ => _serde::__private::Ok(__Field::__ignore),
}
}
fn visit_str<__E>(self, __value: &str) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
"return_code" => _serde::__private::Ok(__Field::__field0),
"return_msg" => _serde::__private::Ok(__Field::__field1),
"detail" => _serde::__private::Ok(__Field::__field2),
"location" => _serde::__private::Ok(__Field::__field3),
"line" => _serde::__private::Ok(__Field::__field4),
_ => _serde::__private::Ok(__Field::__ignore),
}
}
fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
b"return_code" => _serde::__private::Ok(__Field::__field0),
b"return_msg" => _serde::__private::Ok(__Field::__field1),
b"detail" => _serde::__private::Ok(__Field::__field2),
b"location" => _serde::__private::Ok(__Field::__field3),
b"line" => _serde::__private::Ok(__Field::__field4),
_ => _serde::__private::Ok(__Field::__ignore),
}
}
}
impl<'de> _serde::Deserialize<'de> for __Field {
#[inline]
fn deserialize<__D>(__deserializer: __D) -> _serde::__private::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
_serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor)
}
}
#[doc(hidden)]
struct __Visitor<'de> {
marker: _serde::__private::PhantomData<AppError>,
lifetime: _serde::__private::PhantomData<&'de ()>,
}
impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
type Value = AppError;
fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result {
_serde::__private::Formatter::write_str(__formatter, "struct AppError")
}
#[inline]
fn visit_seq<__A>(self, mut __seq: __A) -> _serde::__private::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<'de>,
{
let __field0 = match __try__!(_serde::de::SeqAccess::next_element::<i32>(&mut __seq)) {
_serde::__private::Some(__value) => __value,
_serde::__private::None => {
return _serde::__private::Err(_serde::de::Error::invalid_length(
0usize,
&"struct AppError with 5 elements",
));
}
};
let __field1 = match __try__!(_serde::de::SeqAccess::next_element::<String>(&mut __seq)) {
_serde::__private::Some(__value) => __value,
_serde::__private::None => {
return _serde::__private::Err(_serde::de::Error::invalid_length(
1usize,
&"struct AppError with 5 elements",
));
}
};
let __field2 = match __try__!(_serde::de::SeqAccess::next_element::<String>(&mut __seq)) {
_serde::__private::Some(__value) => __value,
_serde::__private::None => {
return _serde::__private::Err(_serde::de::Error::invalid_length(
2usize,
&"struct AppError with 5 elements",
));
}
};
let __field3 = match __try__!(_serde::de::SeqAccess::next_element::<String>(&mut __seq)) {
_serde::__private::Some(__value) => __value,
_serde::__private::None => {
return _serde::__private::Err(_serde::de::Error::invalid_length(
3usize,
&"struct AppError with 5 elements",
));
}
};
let __field4 = match __try__!(_serde::de::SeqAccess::next_element::<u32>(&mut __seq)) {
_serde::__private::Some(__value) => __value,
_serde::__private::None => {
return _serde::__private::Err(_serde::de::Error::invalid_length(
4usize,
&"struct AppError with 5 elements",
));
}
};
_serde::__private::Ok(AppError {
return_code: HttpStatus::from_code(__field0),
return_msg: I18nString::direct_from_string(__field1),
detail: anyhow::Error::msg(__field2),
location: __field3,
line: __field4,
hierarchy: 0,
backtrace: None,
prost_message: Mutex::new(None),
})
}
#[inline]
fn visit_map<__A>(self, mut __map: __A) -> _serde::__private::Result<Self::Value, __A::Error>
where
__A: _serde::de::MapAccess<'de>,
{
let mut __field0: _serde::__private::Option<i32> = _serde::__private::None;
let mut __field1: _serde::__private::Option<String> = _serde::__private::None;
let mut __field2: _serde::__private::Option<String> = _serde::__private::None;
let mut __field3: _serde::__private::Option<String> = _serde::__private::None;
let mut __field4: _serde::__private::Option<u32> = _serde::__private::None;
while let _serde::__private::Some(__key) = __try__!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) {
match __key {
__Field::__field0 => {
if _serde::__private::Option::is_some(&__field0) {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("return_code"));
}
__field0 = _serde::__private::Some(__try__!(_serde::de::MapAccess::next_value::<i32>(&mut __map)));
}
__Field::__field1 => {
if _serde::__private::Option::is_some(&__field1) {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("return_msg"));
}
__field1 = _serde::__private::Some(__try__!(_serde::de::MapAccess::next_value::<String>(&mut __map)));
}
__Field::__field2 => {
if _serde::__private::Option::is_some(&__field2) {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("detail"));
}
__field2 = _serde::__private::Some(__try__!(_serde::de::MapAccess::next_value::<String>(&mut __map)));
}
__Field::__field3 => {
if _serde::__private::Option::is_some(&__field3) {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("location"));
}
__field3 = _serde::__private::Some(__try__!(_serde::de::MapAccess::next_value::<String>(&mut __map)));
}
__Field::__field4 => {
if _serde::__private::Option::is_some(&__field4) {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("line"));
}
__field4 = _serde::__private::Some(__try__!(_serde::de::MapAccess::next_value::<u32>(&mut __map)));
}
_ => {
let _ = __try__!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map));
}
}
}
let __field0 = match __field0 {
_serde::__private::Some(__field0) => __field0,
_serde::__private::None => __try__!(_serde::__private::de::missing_field("return_code")),
};
let __field1 = match __field1 {
_serde::__private::Some(__field1) => __field1,
_serde::__private::None => __try__!(_serde::__private::de::missing_field("return_msg")),
};
let __field2 = match __field2 {
_serde::__private::Some(__field2) => __field2,
_serde::__private::None => __try__!(_serde::__private::de::missing_field("detail")),
};
let __field3 = match __field3 {
_serde::__private::Some(__field3) => __field3,
_serde::__private::None => __try__!(_serde::__private::de::missing_field("location")),
};
let __field4 = match __field4 {
_serde::__private::Some(__field4) => __field4,
_serde::__private::None => __try__!(_serde::__private::de::missing_field("line")),
};
_serde::__private::Ok(AppError {
return_code: HttpStatus::from_code(__field0),
return_msg: I18nString::direct_from_string(__field1),
detail: anyhow::Error::msg(__field2),
location: __field3,
line: __field4,
hierarchy: 0,
backtrace: None,
prost_message: Mutex::new(None),
})
}
}
#[doc(hidden)]
const FIELDS: &[&str] = &["return_code", "return_msg", "detail", "location", "line"];
_serde::Deserializer::deserialize_struct(
__deserializer,
"AppError",
FIELDS,
__Visitor {
marker: _serde::__private::PhantomData::<AppError>,
lifetime: _serde::__private::PhantomData,
},
)
}
fn deserialize_in_place<__D>(__deserializer: __D, __place: &mut Self) -> _serde::__private::Result<(), __D::Error>
where
__D: _serde::Deserializer<'de>,
{
#[allow(non_camel_case_types)]
#[doc(hidden)]
enum __Field {
__field0,
__field1,
__field2,
__field3,
__field4,
__ignore,
}
#[doc(hidden)]
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result {
_serde::__private::Formatter::write_str(__formatter, "field identifier")
}
fn visit_u64<__E>(self, __value: u64) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
0u64 => _serde::__private::Ok(__Field::__field0),
1u64 => _serde::__private::Ok(__Field::__field1),
2u64 => _serde::__private::Ok(__Field::__field2),
3u64 => _serde::__private::Ok(__Field::__field3),
4u64 => _serde::__private::Ok(__Field::__field4),
_ => _serde::__private::Ok(__Field::__ignore),
}
}
fn visit_str<__E>(self, __value: &str) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
"return_code" => _serde::__private::Ok(__Field::__field0),
"return_msg" => _serde::__private::Ok(__Field::__field1),
"detail" => _serde::__private::Ok(__Field::__field2),
"location" => _serde::__private::Ok(__Field::__field3),
"line" => _serde::__private::Ok(__Field::__field4),
_ => _serde::__private::Ok(__Field::__ignore),
}
}
fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::__private::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
b"return_code" => _serde::__private::Ok(__Field::__field0),
b"return_msg" => _serde::__private::Ok(__Field::__field1),
b"detail" => _serde::__private::Ok(__Field::__field2),
b"location" => _serde::__private::Ok(__Field::__field3),
b"line" => _serde::__private::Ok(__Field::__field4),
_ => _serde::__private::Ok(__Field::__ignore),
}
}
}
impl<'de> _serde::Deserialize<'de> for __Field {
#[inline]
fn deserialize<__D>(__deserializer: __D) -> _serde::__private::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
_serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor)
}
}
#[doc(hidden)]
struct __Visitor<'de, 'place> {
place: &'place mut AppError,
return_code: Option<i32>,
return_msg: Option<String>,
detail: Option<String>,
lifetime: _serde::__private::PhantomData<&'de ()>,
}
impl<'de, 'place> _serde::de::Visitor<'de> for __Visitor<'de, 'place> {
type Value = ();
fn expecting(&self, __formatter: &mut _serde::__private::Formatter) -> _serde::__private::fmt::Result {
_serde::__private::Formatter::write_str(__formatter, "struct AppError")
}
#[inline]
fn visit_seq<__A>(mut self, mut __seq: __A) -> _serde::__private::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<'de>,
{
if __try__!(_serde::de::SeqAccess::next_element_seed(
&mut __seq,
_serde::__private::de::InPlaceSeed(&mut self.return_code)
))
.is_none()
{
return _serde::__private::Err(_serde::de::Error::invalid_length(0usize, &"struct AppError with 5 elements"));
}
if __try__!(_serde::de::SeqAccess::next_element_seed(
&mut __seq,
_serde::__private::de::InPlaceSeed(&mut self.return_msg)
))
.is_none()
{
return _serde::__private::Err(_serde::de::Error::invalid_length(1usize, &"struct AppError with 5 elements"));
}
if __try__!(_serde::de::SeqAccess::next_element_seed(
&mut __seq,
_serde::__private::de::InPlaceSeed(&mut self.detail)
))
.is_none()
{
return _serde::__private::Err(_serde::de::Error::invalid_length(2usize, &"struct AppError with 5 elements"));
}
if __try__!(_serde::de::SeqAccess::next_element_seed(
&mut __seq,
_serde::__private::de::InPlaceSeed(&mut self.place.location)
))
.is_none()
{
return _serde::__private::Err(_serde::de::Error::invalid_length(3usize, &"struct AppError with 5 elements"));
}
if __try__!(_serde::de::SeqAccess::next_element_seed(
&mut __seq,
_serde::__private::de::InPlaceSeed(&mut self.place.line)
))
.is_none()
{
return _serde::__private::Err(_serde::de::Error::invalid_length(4usize, &"struct AppError with 5 elements"));
}
_serde::__private::Ok(())
}
#[inline]
fn visit_map<__A>(mut self, mut __map: __A) -> _serde::__private::Result<Self::Value, __A::Error>
where
__A: _serde::de::MapAccess<'de>,
{
let mut __field0: bool = false;
let mut __field1: bool = false;
let mut __field2: bool = false;
let mut __field3: bool = false;
let mut __field4: bool = false;
while let _serde::__private::Some(__key) = __try__!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) {
match __key {
__Field::__field0 => {
if __field0 {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("return_code"));
}
__try__!(_serde::de::MapAccess::next_value_seed(
&mut __map,
_serde::__private::de::InPlaceSeed(&mut self.return_code)
));
__field0 = true;
}
__Field::__field1 => {
if __field1 {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("return_msg"));
}
__try__!(_serde::de::MapAccess::next_value_seed(
&mut __map,
_serde::__private::de::InPlaceSeed(&mut self.return_msg)
));
__field1 = true;
}
__Field::__field2 => {
if __field2 {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("detail"));
}
__try__!(_serde::de::MapAccess::next_value_seed(
&mut __map,
_serde::__private::de::InPlaceSeed(&mut self.detail)
));
__field2 = true;
}
__Field::__field3 => {
if __field3 {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("location"));
}
__try__!(_serde::de::MapAccess::next_value_seed(
&mut __map,
_serde::__private::de::InPlaceSeed(&mut self.place.location)
));
__field3 = true;
}
__Field::__field4 => {
if __field4 {
return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field("line"));
}
__try__!(_serde::de::MapAccess::next_value_seed(
&mut __map,
_serde::__private::de::InPlaceSeed(&mut self.place.line)
));
__field4 = true;
}
_ => {
let _ = __try__!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map));
}
}
}
if !__field0 {
self.return_code = __try__!(_serde::__private::de::missing_field("return_code"));
};
if !__field1 {
self.return_msg = __try__!(_serde::__private::de::missing_field("return_msg"));
};
if !__field2 {
self.detail = __try__!(_serde::__private::de::missing_field("detail"));
};
if !__field3 {
self.place.location = __try__!(_serde::__private::de::missing_field("location"));
};
if !__field4 {
self.place.line = __try__!(_serde::__private::de::missing_field("line"));
};
self.place.return_code = self
.return_code
.map(HttpStatus::from_code)
.ok_or_else(|| serde::de::Error::custom("missing failed: return_code"))?;
self.place.return_msg = self
.return_msg
.map(I18nString::direct_from_string)
.ok_or_else(|| serde::de::Error::custom("missing failed: return_msg"))?;
self.place.detail =
self.detail.map(anyhow::Error::msg).ok_or_else(|| serde::de::Error::custom("missing failed: detail"))?;
_serde::__private::Ok(())
}
}
#[doc(hidden)]
const FIELDS: &[&str] = &["return_code", "return_msg", "detail", "location", "line"];
_serde::Deserializer::deserialize_struct(
__deserializer,
"AppError",
FIELDS,
__Visitor {
place: __place,
return_code: None,
return_msg: None,
detail: None,
lifetime: _serde::__private::PhantomData,
},
)
}
}
};
}
mod message {
use std::sync::{Mutex, MutexGuard};
use prost::Message;
use crate::tina::{
data::{http_status::HttpStatus, i18n_string::I18nString, return_code::IReturnCode},
i18n::ResourceBundle,
};
use super::AppError;
#[allow(clippy::derive_partial_eq_without_eq, clippy::unwrap_used)]
#[derive(Clone, PartialEq, prost::Message)]
pub(crate) struct AppErrorMessage {
#[prost(int32, tag = "1")]
pub(crate) return_code: i32,
#[prost(string, tag = "2")]
pub(crate) return_msg: prost::alloc::string::String,
#[prost(string, tag = "3")]
pub(crate) detail: prost::alloc::string::String,
#[prost(string, tag = "4")]
pub(crate) location: prost::alloc::string::String,
#[prost(uint32, tag = "5")]
pub(crate) line: u32,
}
impl Default for AppError {
fn default() -> Self {
Self {
return_code: HttpStatus::from_code(200),
return_msg: I18nString::direct_from(""),
detail: anyhow::Error::msg(""),
location: Default::default(),
line: Default::default(),
hierarchy: Default::default(),
backtrace: Default::default(),
prost_message: Default::default(),
}
}
}
impl AppError {
pub(crate) fn get_prost_message(&self) -> MutexGuard<'_, Option<AppErrorMessage>> {
let mut lock = self.prost_message.lock().expect("lock AppErrorMessage failed");
if lock.is_none() {
let return_code = self.return_code.get_code();
let return_msg = self.return_msg.get_string(ResourceBundle::get_default_locale().as_str());
let detail = self.detail.to_string();
let location = self.location.to_string();
let line = self.line;
let prost_message = AppErrorMessage {
return_code,
return_msg,
detail,
location,
line,
};
(*lock) = Some(prost_message);
}
lock
}
}
impl From<AppError> for AppErrorMessage {
fn from(value: AppError) -> Self {
let return_code = value.return_code.get_code();
let return_msg = value.return_msg.get_string(ResourceBundle::get_default_locale().as_str());
let detail = value.detail.to_string();
let location = value.location;
let line = value.line;
Self {
return_code,
return_msg,
detail,
location,
line,
}
}
}
impl From<AppErrorMessage> for AppError {
fn from(value: AppErrorMessage) -> Self {
let prost_message = value.clone();
let AppErrorMessage {
return_code,
return_msg,
detail,
location,
line,
} = value;
Self {
return_code: HttpStatus::from_code(return_code),
return_msg: I18nString::direct_from_string(return_msg),
detail: anyhow::Error::msg(detail),
location,
line,
hierarchy: 0,
backtrace: None,
prost_message: Mutex::new(Some(prost_message)),
}
}
}
impl Message for AppError {
fn encode_raw<B>(&self, buf: &mut B)
where
B: bytes::BufMut,
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_ref() {
message.encode_raw(buf);
}
}
fn merge_field<B>(
&mut self,
tag: u32,
wire_type: prost::encoding::WireType,
buf: &mut B,
ctx: prost::encoding::DecodeContext,
) -> Result<(), prost::DecodeError>
where
B: bytes::Buf,
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_mut() {
return message.merge_field(tag, wire_type, buf, ctx);
}
Ok(())
}
fn encoded_len(&self) -> usize {
if let Some(message) = self.get_prost_message().as_ref() {
return message.encoded_len();
}
0
}
fn clear(&mut self) {
if let Some(message) = self.get_prost_message().as_mut() {
message.clear();
}
}
fn encode_to_vec(&self) -> Vec<u8>
where
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_ref() {
return message.encode_to_vec();
}
vec![]
}
fn encode_length_delimited_to_vec(&self) -> Vec<u8>
where
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_ref() {
return message.encode_length_delimited_to_vec();
}
vec![]
}
fn decode<B>(buf: B) -> Result<Self, prost::DecodeError>
where
B: bytes::Buf,
Self: Default,
{
let message = AppErrorMessage::decode(buf)?;
Ok(AppError::from(message))
}
fn decode_length_delimited<B>(buf: B) -> Result<Self, prost::DecodeError>
where
B: bytes::Buf,
Self: Default,
{
let message = AppErrorMessage::decode_length_delimited(buf)?;
Ok(AppError::from(message))
}
fn merge<B>(&mut self, buf: B) -> Result<(), prost::DecodeError>
where
B: bytes::Buf,
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_mut() {
message.merge(buf)?;
}
Ok(())
}
fn merge_length_delimited<B>(&mut self, buf: B) -> Result<(), prost::DecodeError>
where
B: bytes::Buf,
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_mut() {
message.merge_length_delimited(buf)?;
}
Ok(())
}
fn encode<B>(&self, buf: &mut B) -> Result<(), prost::EncodeError>
where
B: bytes::BufMut,
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_ref() {
message.encode(buf)?;
}
Ok(())
}
fn encode_length_delimited<B>(&self, buf: &mut B) -> Result<(), prost::EncodeError>
where
B: bytes::BufMut,
Self: Sized,
{
if let Some(message) = self.get_prost_message().as_ref() {
message.encode_length_delimited(buf)?;
}
Ok(())
}
}
}
impl PartialEq for AppError {
fn eq(&self, other: &Self) -> bool {
self.return_code.get_code() == other.return_code.get_code()
&& self.return_msg == other.return_msg
&& self.location == other.location
&& self.line == other.line
}
}
impl crate::serde::ser::Error for AppError {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
AppError::new_system_error(anyhow!("{}", msg), file!(), line!())
}
}
impl crate::serde::de::Error for AppError {
fn custom<T>(msg: T) -> Self
where
T: Display,
{
AppError::new_system_error(anyhow!("{}", msg), file!(), line!())
}
}
#[cfg(feature = "rbatis")]
impl From<AppError> for ::rbatis::Error {
fn from(value: AppError) -> Self {
::rbatis::Error::E(format!("{:?}", value))
}
}
#[allow(unused_imports, unused_variables)]
mod convert {
use crate::i18n_string;
use crate::tina::data::app_error::AppError;
use crate::tina::data::http_status::HttpStatus;
use crate::tina::i18n::message::system_message::SystemMessage;
use anyhow::anyhow;
use either::Either;
use std::any::{type_name, Any, TypeId};
pub trait IntoAppError: 'static {
fn try_into_app_error(self, location: &str, line: u32) -> Either<AppError, Self>
where
Self: Sized;
}
impl<T: std::error::Error + 'static> IntoAppError for T {
fn try_into_app_error(self, location: &str, line: u32) -> Either<AppError, Self>
where
Self: Sized,
{
#[cfg(feature = "rbatis")]
if TypeId::of::<T>() == TypeId::of::<rbatis::Error>() {
let err = Box::new(self) as Box<dyn Any>;
let rbatis_err = err
.downcast::<rbatis::Error>()
.unwrap_or_else(|_| panic!("convert type to '{}' failed: {}", type_name::<rbatis::Error>(), type_name::<T>()));
return match &*rbatis_err {
rbatis::Error::E(msg) => match msg.contains(": 1062 ") {
true => Either::Left(AppError::new(
HttpStatus::Error,
i18n_string!(SystemMessage::ERROR_RECORD_EXISTS),
anyhow!("{}", msg),
location,
line,
)),
false => {
let err = rbatis_err as Box<dyn Any>;
let other: Box<T> = err.downcast::<T>().unwrap_or_else(|_| panic!("convert type failed: {}", type_name::<T>()));
Either::Right(*other)
}
},
};
}
#[cfg(feature = "server-actix-web")]
{
super::server_actix_web::try_from_actix_web_error(self, location, line)
}
#[cfg(not(feature = "server-actix-web"))]
{
Either::Right(self)
}
}
}
}
#[cfg(feature = "server-actix-web")]
mod server_actix_web {
use crate::tina::data::app_error::AppError;
use either::Either;
use std::any::{type_name, Any};
pub(in crate::tina::data::app_error) fn try_from_actix_web_error<T: std::error::Error + 'static>(
err: T,
location: &str,
line: u32,
) -> Either<AppError, T> {
let err = Box::new(err) as Box<dyn Any>;
match err.downcast::<actix_web::Error>() {
Ok(err) => match err.as_error::<AppError>() {
None => {
let err = err as Box<dyn Any>;
let other: Box<T> = err.downcast::<T>().unwrap_or_else(|_| panic!("convert type failed: {}", type_name::<T>()));
Either::Right(*other)
}
Some(app_err) => {
let err = AppError::from_app_err(app_err, location, line);
Either::Left(err)
}
},
Err(err) => {
let other: Box<T> = err.downcast::<T>().unwrap_or_else(|_| panic!("convert type failed: {}", type_name::<T>()));
Either::Right(*other)
}
}
}
}
#[cfg(feature = "server-axum")]
mod server_axum {
use crate::app_system_error;
use super::AppError;
impl From<axum::Error> for AppError {
fn from(value: axum::Error) -> Self {
let inner = value.into_inner();
match inner.downcast::<AppError>() {
Ok(v) => *v,
Err(err) => app_system_error!("{:?}", err),
}
}
}
}
#[cfg(test)]
mod test {
use bytes::{Bytes, BytesMut};
use prost::Message;
use serde::Deserialize;
use crate::{
app_error_from, app_system_error,
tina::{data::AppResult, i18n::ResourceBundle},
};
use super::AppError;
#[test]
fn serde() -> AppResult<()> {
let err = app_system_error!("test error");
let locale = ResourceBundle::get_default_locale();
{
let v = bincode::serialize(&err).map_err(app_error_from!())?;
let err2 = bincode::deserialize::<AppError>(&v).map_err(app_error_from!())?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
{
let v = serde_json::to_string(&err).map_err(app_error_from!())?;
let err2 = serde_json::from_str::<AppError>(&v).map_err(app_error_from!())?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
{
let v = serde_qs::to_string(&err).map_err(app_error_from!())?;
let err2 = serde_qs::from_str::<AppError>(&v).map_err(app_error_from!())?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
{
let v = serde_value::to_value(&err).map_err(app_error_from!())?;
let err2 = AppError::deserialize(serde_value::ValueDeserializer::<AppError>::new(v)).map_err(app_error_from!())?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
Ok(())
}
#[test]
fn prost() -> Result<(), Box<dyn std::error::Error>> {
let err = app_system_error!("test error");
let locale = ResourceBundle::get_default_locale();
{
let encode_len = Message::encoded_len(&err);
let mut buf = BytesMut::with_capacity(encode_len);
err.encode(&mut buf)?;
let buf = buf.freeze();
let err2 = AppError::decode(buf)?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
{
let encode_len = Message::encoded_len(&err);
let mut buf = BytesMut::with_capacity(encode_len);
err.encode_length_delimited(&mut buf)?;
let buf = buf.freeze();
let err2 = AppError::decode_length_delimited(buf)?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
{
let buf = err.encode_to_vec();
let buf = Bytes::from(buf);
let err2 = AppError::decode(buf)?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
{
let buf = err.encode_length_delimited_to_vec();
let buf = Bytes::from(buf);
let err2 = AppError::decode_length_delimited(buf)?;
assert_eq!(err.return_code.get_code(), err2.return_code.get_code());
assert_eq!(err.return_msg.get_string(locale.as_str()), err2.return_msg.get_string(locale.as_str()));
assert_eq!(err.detail.to_string(), err2.detail.to_string());
assert_eq!(err.location, err2.location);
assert_eq!(err.line, err2.line);
}
Ok(())
}
}