Repository: juspay/hyperswitch
File: crates/common_utils/src/ext_traits.rs (Rust)

Code before:
        &'de self,
        type_name: &'static str,
    ) -> CustomResult<T, errors::ParsingError>
    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 `<T>` by using `serde::Deserialize`
    fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
    where
        T: serde::de::DeserializeOwned;
}

impl ValueExt for serde_json::Value {
    fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
    where
        T: serde::de::DeserializeOwned,
    {
        let debug = format!("Unable to parse {type_name} from serde_json::Value");
        serde_json::from_value::<T>(self)
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| debug)
    }
}

impl<MaskingStrategy> ValueExt for Secret<serde_json::Value, MaskingStrategy>
where
    MaskingStrategy: Strategy<serde_json::Value>,
{
    fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
    where
        T: serde::de::DeserializeOwned,
    {
        self.expose().parse_value(type_name)
    }
}

impl<E: ValueExt + Clone> ValueExt for crypto::Encryptable<E> {
    fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
    where
        T: serde::de::DeserializeOwned,
    {
        self.into_inner().parse_value(type_name)
    }

Review comment (bug):
Can we atleast try to log the available keys here? It would be very hard to debug any PROD issue without any context of original data point 
cc: @jarnura @SanchithHegde 

Code after:
        T: Deserialize<'de>;
}

impl ByteSliceExt for [u8] {
    #[track_caller]
    fn parse_struct<'de, T>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult<T, errors::ParsingError>
    where
        T: Deserialize<'de>,
    {
        serde_json::from_slice(self)
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| {
                let value = serde_json::from_slice::<serde_json::Value>(self)
                    .unwrap_or_else(|_| serde_json::Value::String(String::new()));

                format!(
                    "Unable to parse {type_name} from &[u8] {:?}",
                    Secret::<_, masking::JsonMaskStrategy>::new(value)
                )
            })
    }
}

/// Extending functionalities of `serde_json::Value` for performing parsing
pub trait ValueExt {
    /// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize`
    fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
    where
        T: serde::de::DeserializeOwned;
}

impl ValueExt for serde_json::Value {
    fn parse_value<T>(self, type_name: &'static str) -> CustomResult<T, errors::ParsingError>
    where
        T: serde::de::DeserializeOwned,
    {
        serde_json::from_value::<T>(self.clone())
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| {
                format!(
                    "Unable to parse {type_name} from serde_json::Value: {:?}",
                    // Required to prevent logging sensitive data in case of deserialization failure
                    Secret::<_, masking::JsonMaskStrategy>::new(self)
                )
            })
    }
}