refactor(errors): add parsing error types for context info (#911)

This commit is contained in:
Sampras Lopes
2023-05-11 18:15:00 +05:30
committed by GitHub
parent f790099368
commit 0d46690502
9 changed files with 77 additions and 67 deletions

View File

@ -8,36 +8,23 @@
/// ///
pub type CustomResult<T, E> = error_stack::Result<T, E>; pub type CustomResult<T, E> = error_stack::Result<T, E>;
macro_rules! impl_error_display { /// Parsing Errors
($st: ident, $arg: tt) => { #[derive(Debug, thiserror::Error)]
impl std::fmt::Display for $st { pub enum ParsingError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ///Failed to parse enum
fmt.write_str(&format!( #[error("Failed to parse enum: {0}")]
"{{ error_type: {:?}, error_description: {} }}", EnumParseFailure(&'static str),
self, $arg ///Failed to parse struct
)) #[error("Failed to parse struct: {0}")]
} StructParseFailure(&'static str),
} /// Failed to encode data to given format
}; #[error("Failed to serialize to {0} format")]
EncodeError(&'static str),
/// Failed to parse data
#[error("Unknown error while parsing")]
UnknownError,
} }
macro_rules! impl_error_type {
($name: ident, $arg: tt) => {
#[doc = ""]
#[doc = stringify!(Error variant $name)]
#[doc = stringify!(Custom error variant for $arg)]
#[doc = ""]
#[derive(Debug)]
pub struct $name;
impl_error_display!($name, $arg);
impl std::error::Error for $name {}
};
}
impl_error_type!(ParsingError, "Parsing error");
/// Validation errors. /// Validation errors.
#[allow(missing_docs)] // Only to prevent warnings about struct fields not being documented #[allow(missing_docs)] // Only to prevent warnings about struct fields not being documented
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]

View File

@ -85,10 +85,12 @@ where
Result<P, <P as TryFrom<&'e Self>>::Error>: ResultExt, Result<P, <P as TryFrom<&'e Self>>::Error>: ResultExt,
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize, <Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize,
{ {
serde_json::to_string(&P::try_from(self).change_context(errors::ParsingError)?) serde_json::to_string(
.into_report() &P::try_from(self).change_context(errors::ParsingError::UnknownError)?,
.change_context(errors::ParsingError) )
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request")) .into_report()
.change_context(errors::ParsingError::EncodeError("string"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
} }
fn convert_and_url_encode(&'e self) -> CustomResult<String, errors::ParsingError> fn convert_and_url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
@ -97,10 +99,12 @@ where
Result<P, <P as TryFrom<&'e Self>>::Error>: ResultExt, Result<P, <P as TryFrom<&'e Self>>::Error>: ResultExt,
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize, <Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize,
{ {
serde_urlencoded::to_string(&P::try_from(self).change_context(errors::ParsingError)?) serde_urlencoded::to_string(
.into_report() &P::try_from(self).change_context(errors::ParsingError::UnknownError)?,
.change_context(errors::ParsingError) )
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request")) .into_report()
.change_context(errors::ParsingError::EncodeError("url-encoded"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
} }
// Check without two functions can we combine this // Check without two functions can we combine this
@ -110,7 +114,7 @@ where
{ {
serde_urlencoded::to_string(self) serde_urlencoded::to_string(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::EncodeError("url-encoded"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request")) .attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
} }
@ -120,7 +124,7 @@ where
{ {
serde_json::to_string(self) serde_json::to_string(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::EncodeError("json"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request")) .attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
} }
@ -130,7 +134,7 @@ where
{ {
serde_json::to_value(self) serde_json::to_value(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::EncodeError("json-value"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a value")) .attach_printable_lazy(|| format!("Unable to convert {self:?} to a value"))
} }
@ -140,7 +144,7 @@ where
{ {
serde_json::to_vec(self) serde_json::to_vec(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::EncodeError("byte-vec"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a value")) .attach_printable_lazy(|| format!("Unable to convert {self:?} to a value"))
} }
} }
@ -152,13 +156,19 @@ pub trait BytesExt {
/// ///
/// Convert `bytes::Bytes` into type `<T>` using `serde::Deserialize` /// Convert `bytes::Bytes` into type `<T>` using `serde::Deserialize`
/// ///
fn parse_struct<'de, T>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>; T: Deserialize<'de>;
} }
impl BytesExt for bytes::Bytes { impl BytesExt for bytes::Bytes {
fn parse_struct<'de, T>(&'de self, _type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>, T: Deserialize<'de>,
{ {
@ -166,7 +176,7 @@ impl BytesExt for bytes::Bytes {
serde_json::from_slice::<T>(self.chunk()) serde_json::from_slice::<T>(self.chunk())
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| { .attach_printable_lazy(|| {
let variable_type = std::any::type_name::<T>(); let variable_type = std::any::type_name::<T>();
format!("Unable to parse {variable_type} from bytes {self:?}") format!("Unable to parse {variable_type} from bytes {self:?}")
@ -181,19 +191,25 @@ pub trait ByteSliceExt {
/// ///
/// Convert `[u8]` into type `<T>` by using `serde::Deserialize` /// Convert `[u8]` into type `<T>` by using `serde::Deserialize`
/// ///
fn parse_struct<'de, T>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>; T: Deserialize<'de>;
} }
impl ByteSliceExt for [u8] { impl ByteSliceExt for [u8] {
fn parse_struct<'de, T>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>, T: Deserialize<'de>,
{ {
serde_json::from_slice(self) serde_json::from_slice(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| format!("Unable to parse {type_name} from &[u8] {:?}", &self)) .attach_printable_lazy(|| format!("Unable to parse {type_name} from &[u8] {:?}", &self))
} }
} }
@ -205,13 +221,13 @@ pub trait ValueExt {
/// ///
/// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize` /// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize`
/// ///
fn parse_value<T>(self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
where where
T: serde::de::DeserializeOwned; T: serde::de::DeserializeOwned;
} }
impl ValueExt for serde_json::Value { impl ValueExt for serde_json::Value {
fn parse_value<T>(self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,
{ {
@ -221,7 +237,7 @@ impl ValueExt for serde_json::Value {
); );
serde_json::from_value::<T>(self) serde_json::from_value::<T>(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| debug) .attach_printable_lazy(|| debug)
} }
} }
@ -230,7 +246,7 @@ impl<MaskingStrategy> ValueExt for Secret<serde_json::Value, MaskingStrategy>
where where
MaskingStrategy: Strategy<serde_json::Value>, MaskingStrategy: Strategy<serde_json::Value>,
{ {
fn parse_value<T>(self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,
{ {
@ -245,7 +261,7 @@ pub trait StringExt<T> {
/// ///
/// Convert `String` into type `<T>` (which being an `enum`) /// Convert `String` into type `<T>` (which being an `enum`)
/// ///
fn parse_enum(self, enum_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_enum(self, enum_name: &'static str) -> CustomResult<T, errors::ParsingError>
where where
T: std::str::FromStr, T: std::str::FromStr,
// Requirement for converting the `Err` variant of `FromStr` to `Report<Err>` // Requirement for converting the `Err` variant of `FromStr` to `Report<Err>`
@ -254,30 +270,36 @@ pub trait StringExt<T> {
/// ///
/// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize` /// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize`
/// ///
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de>(
&'de self,
type_name: &'static str,
) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>; T: Deserialize<'de>;
} }
impl<T> StringExt<T> for String { impl<T> StringExt<T> for String {
fn parse_enum(self, enum_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_enum(self, enum_name: &'static str) -> CustomResult<T, errors::ParsingError>
where where
T: std::str::FromStr, T: std::str::FromStr,
<T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static, <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
{ {
T::from_str(&self) T::from_str(&self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::EnumParseFailure(enum_name))
.attach_printable_lazy(|| format!("Invalid enum variant {self:?} for enum {enum_name}")) .attach_printable_lazy(|| format!("Invalid enum variant {self:?} for enum {enum_name}"))
} }
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError> fn parse_struct<'de>(
&'de self,
type_name: &'static str,
) -> CustomResult<T, errors::ParsingError>
where where
T: Deserialize<'de>, T: Deserialize<'de>,
{ {
serde_json::from_str::<T>(self) serde_json::from_str::<T>(self)
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| { .attach_printable_lazy(|| {
format!("Unable to parse {type_name} from string {:?}", &self) format!("Unable to parse {type_name} from string {:?}", &self)
}) })

View File

@ -133,7 +133,7 @@ impl super::RedisConnectionPool {
pub async fn get_and_deserialize_key<T>( pub async fn get_and_deserialize_key<T>(
&self, &self,
key: &str, key: &str,
type_name: &str, type_name: &'static str,
) -> CustomResult<T, errors::RedisError> ) -> CustomResult<T, errors::RedisError>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,
@ -380,7 +380,7 @@ impl super::RedisConnectionPool {
&self, &self,
key: &str, key: &str,
field: &str, field: &str,
type_name: &str, type_name: &'static str,
) -> CustomResult<V, errors::RedisError> ) -> CustomResult<V, errors::RedisError>
where where
V: serde::de::DeserializeOwned, V: serde::de::DeserializeOwned,

View File

@ -735,7 +735,8 @@ fn get_webhook_object_from_body(
.notification_items .notification_items
.drain(..) .drain(..)
.next() .next()
.ok_or(errors::ParsingError) // TODO: ParsingError doesn't seem to be an apt error for this case
.ok_or(errors::ParsingError::UnknownError)
.into_report()?; .into_report()?;
Ok(item_object.notification_request_item) Ok(item_object.notification_request_item)

View File

@ -106,7 +106,7 @@ impl StorageInterface for MockDb {}
pub async fn get_and_deserialize_key<T>( pub async fn get_and_deserialize_key<T>(
db: &dyn StorageInterface, db: &dyn StorageInterface,
key: &str, key: &str,
type_name: &str, type_name: &'static str,
) -> common_utils::errors::CustomResult<T, redis_interface::errors::RedisError> ) -> common_utils::errors::CustomResult<T, redis_interface::errors::RedisError>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,

View File

@ -48,7 +48,7 @@ impl ConnectorAccessToken for Store {
let access_token: Option<types::AccessToken> = maybe_token let access_token: Option<types::AccessToken> = maybe_token
.map(|token| token.parse_struct("AccessToken")) .map(|token| token.parse_struct("AccessToken"))
.transpose() .transpose()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::UnknownError)
.change_context(errors::StorageError::DeserializationFailed)?; .change_context(errors::StorageError::DeserializationFailed)?;
Ok(access_token) Ok(access_token)

View File

@ -83,7 +83,7 @@ impl ProcessTrackerBatch {
.as_str() .as_str()
.parse() .parse()
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::UnknownError)
.change_context(errors::ProcessTrackerError::DeserializationFailed)?, .change_context(errors::ProcessTrackerError::DeserializationFailed)?,
) )
.into_report() .into_report()
@ -105,7 +105,7 @@ impl ProcessTrackerBatch {
let trackers = serde_json::from_str::<Vec<ProcessTracker>>(trackers.as_str()) let trackers = serde_json::from_str::<Vec<ProcessTracker>>(trackers.as_str())
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::UnknownError)
.attach_printable_lazy(|| { .attach_printable_lazy(|| {
format!("Unable to parse trackers from JSON string: {trackers:?}") format!("Unable to parse trackers from JSON string: {trackers:?}")
}) })

View File

@ -76,7 +76,7 @@ pub fn generate_id(length: usize, prefix: &str) -> String {
pub trait ConnectorResponseExt: Sized { pub trait ConnectorResponseExt: Sized {
fn get_response(self) -> RouterResult<types::Response>; fn get_response(self) -> RouterResult<types::Response>;
fn get_error_response(self) -> RouterResult<types::Response>; fn get_error_response(self) -> RouterResult<types::Response>;
fn get_response_inner<T: DeserializeOwned>(self, type_name: &str) -> RouterResult<T> { fn get_response_inner<T: DeserializeOwned>(self, type_name: &'static str) -> RouterResult<T> {
self.get_response()? self.get_response()?
.response .response
.parse_struct(type_name) .parse_struct(type_name)

View File

@ -57,11 +57,11 @@ where
{ {
let value = self let value = self
.get_required_value(enum_name) .get_required_value(enum_name)
.change_context(errors::ParsingError)?; .change_context(errors::ParsingError::UnknownError)?;
E::from_str(value.as_ref()) E::from_str(value.as_ref())
.into_report() .into_report()
.change_context(errors::ParsingError) .change_context(errors::ParsingError::UnknownError)
.attach_printable_lazy(|| format!("Invalid {{ {enum_name}: {value:?} }} ")) .attach_printable_lazy(|| format!("Invalid {{ {enum_name}: {value:?} }} "))
} }
@ -72,7 +72,7 @@ where
{ {
let value = self let value = self
.get_required_value(type_name) .get_required_value(type_name)
.change_context(errors::ParsingError)?; .change_context(errors::ParsingError::UnknownError)?;
value.parse_value(type_name) value.parse_value(type_name)
} }