//! This module holds traits for extending functionalities for existing datatypes
//! & inbuilt datatypes.
use error_stack::ResultExt;
use masking::{ExposeInterface, PeekInterface, Secret, Strategy};
use quick_xml::de;
#[cfg(all(feature = "logs", feature = "async_ext"))]
use router_env::logger;
use serde::{Deserialize, Serialize};
use crate::{
crypto,
errors::{self, CustomResult},
fp_utils::when,
};
/// Encode interface
/// An interface for performing type conversions and serialization
pub trait Encode<'e>
where
Self: 'e + std::fmt::Debug,
{
// If needed get type information/custom error implementation.
///
/// Converting `Self` into an intermediate representation `
`
/// and then performing encoding operation using the `Serialize` trait from `serde`
/// Specifically to convert into json, by using `serde_json`
fn convert_and_encode
(&'e self) -> CustomResult
where
P: TryFrom<&'e Self> + Serialize,
Result>::Error>: ResultExt,
>::Error> as ResultExt>::Ok: Serialize;
/// Converting `Self` into an intermediate representation ``
/// and then performing encoding operation using the `Serialize` trait from `serde`
/// Specifically, to convert into urlencoded, by using `serde_urlencoded`
fn convert_and_url_encode
(&'e self) -> CustomResult
where
P: TryFrom<&'e Self> + Serialize,
Result>::Error>: ResultExt,
>::Error> as ResultExt>::Ok: Serialize;
/// Functionality, for specifically encoding `Self` into `String`
/// after serialization by using `serde::Serialize`
fn url_encode(&'e self) -> CustomResult
where
Self: Serialize;
/// Functionality, for specifically encoding `Self` into `String`
/// after serialization by using `serde::Serialize`
/// specifically, to convert into JSON `String`.
fn encode_to_string_of_json(&'e self) -> CustomResult
where
Self: Serialize;
/// Functionality, for specifically encoding `Self` into `String`
/// after serialization by using `serde::Serialize`
/// specifically, to convert into XML `String`.
fn encode_to_string_of_xml(&'e self) -> CustomResult
where
Self: Serialize;
/// Functionality, for specifically encoding `Self` into `serde_json::Value`
/// after serialization by using `serde::Serialize`
fn encode_to_value(&'e self) -> CustomResult
where
Self: Serialize;
/// Functionality, for specifically encoding `Self` into `Vec`
/// after serialization by using `serde::Serialize`
fn encode_to_vec(&'e self) -> CustomResult, errors::ParsingError>
where
Self: Serialize;
}
impl<'e, A> Encode<'e> for A
where
Self: 'e + std::fmt::Debug,
{
fn convert_and_encode(&'e self) -> CustomResult
where
P: TryFrom<&'e Self> + Serialize,
Result>::Error>: ResultExt,
>::Error> as ResultExt>::Ok: Serialize,
{
serde_json::to_string(
&P::try_from(self).change_context(errors::ParsingError::UnknownError)?,
)
.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
where
P: TryFrom<&'e Self> + Serialize,
Result>::Error>: ResultExt,
>::Error> as ResultExt>::Ok: Serialize,
{
serde_urlencoded::to_string(
&P::try_from(self).change_context(errors::ParsingError::UnknownError)?,
)
.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
fn url_encode(&'e self) -> CustomResult
where
Self: Serialize,
{
serde_urlencoded::to_string(self)
.change_context(errors::ParsingError::EncodeError("url-encoded"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
}
fn encode_to_string_of_json(&'e self) -> CustomResult
where
Self: Serialize,
{
serde_json::to_string(self)
.change_context(errors::ParsingError::EncodeError("json"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
}
fn encode_to_string_of_xml(&'e self) -> CustomResult
where
Self: Serialize,
{
quick_xml::se::to_string(self)
.change_context(errors::ParsingError::EncodeError("xml"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
}
fn encode_to_value(&'e self) -> CustomResult
where
Self: Serialize,
{
serde_json::to_value(self)
.change_context(errors::ParsingError::EncodeError("json-value"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a value"))
}
fn encode_to_vec(&'e self) -> CustomResult, errors::ParsingError>
where
Self: Serialize,
{
serde_json::to_vec(self)
.change_context(errors::ParsingError::EncodeError("byte-vec"))
.attach_printable_lazy(|| format!("Unable to convert {self:?} to a value"))
}
}
/// Extending functionalities of `bytes::Bytes`
pub trait BytesExt {
/// Convert `bytes::Bytes` into type `` using `serde::Deserialize`
fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult
where
T: Deserialize<'de>;
}
impl BytesExt for bytes::Bytes {
fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult
where
T: Deserialize<'de>,
{
use bytes::Buf;
serde_json::from_slice::(self.chunk())
.change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| {
let variable_type = std::any::type_name::();
format!("Unable to parse {variable_type} from bytes {self:?}")
})
}
}
/// Extending functionalities of `[u8]` for performing parsing
pub trait ByteSliceExt {
/// Convert `[u8]` into type `` by using `serde::Deserialize`
fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult
where
T: Deserialize<'de>;
}
impl ByteSliceExt for [u8] {
#[track_caller]
fn parse_struct<'de, T>(
&'de self,
type_name: &'static str,
) -> CustomResult
where
T: Deserialize<'de>,
{
serde_json::from_slice(self)
.change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| format!("Unable to parse {type_name} from &[u8] {:?}", &self))
}
}
/// Extending functionalities of `serde_json::Value` for performing parsing
pub trait ValueExt {
/// Convert `serde_json::Value` into type `` by using `serde::Deserialize`
fn parse_value(self, type_name: &'static str) -> CustomResult
where
T: serde::de::DeserializeOwned;
}
impl ValueExt for serde_json::Value {
fn parse_value(self, type_name: &'static str) -> CustomResult
where
T: serde::de::DeserializeOwned,
{
let debug = format!(
"Unable to parse {type_name} from serde_json::Value: {:?}",
&self
);
serde_json::from_value::(self)
.change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| debug)
}
}
impl ValueExt for Secret
where
MaskingStrategy: Strategy,
{
fn parse_value(self, type_name: &'static str) -> CustomResult
where
T: serde::de::DeserializeOwned,
{
self.expose().parse_value(type_name)
}
}
impl ValueExt for crypto::Encryptable {
fn parse_value(self, type_name: &'static str) -> CustomResult
where
T: serde::de::DeserializeOwned,
{
self.into_inner().parse_value(type_name)
}
}
/// Extending functionalities of `String` for performing parsing
pub trait StringExt {
/// Convert `String` into type `` (which being an `enum`)
fn parse_enum(self, enum_name: &'static str) -> CustomResult
where
T: std::str::FromStr,
// Requirement for converting the `Err` variant of `FromStr` to `Report`
::Err: std::error::Error + Send + Sync + 'static;
/// Convert `serde_json::Value` into type `` by using `serde::Deserialize`
fn parse_struct<'de>(
&'de self,
type_name: &'static str,
) -> CustomResult
where
T: Deserialize<'de>;
}
impl StringExt for String {
fn parse_enum(self, enum_name: &'static str) -> CustomResult
where
T: std::str::FromStr,
::Err: std::error::Error + Send + Sync + 'static,
{
T::from_str(&self)
.change_context(errors::ParsingError::EnumParseFailure(enum_name))
.attach_printable_lazy(|| format!("Invalid enum variant {self:?} for enum {enum_name}"))
}
fn parse_struct<'de>(
&'de self,
type_name: &'static str,
) -> CustomResult
where
T: Deserialize<'de>,
{
serde_json::from_str::(self)
.change_context(errors::ParsingError::StructParseFailure(type_name))
.attach_printable_lazy(|| {
format!("Unable to parse {type_name} from string {:?}", &self)
})
}
}
/// Extending functionalities of Wrapper types for idiomatic
#[cfg(feature = "async_ext")]
#[cfg_attr(feature = "async_ext", async_trait::async_trait)]
pub trait AsyncExt {
/// Output type of the map function
type WrappedSelf;
/// Extending map by allowing functions which are async
async fn async_map(self, func: F) -> Self::WrappedSelf
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future