use alloc::{string::ToString, vec::Vec};
use base64::{Engine, engine::general_purpose::STANDARD};
use serde::{
Deserialize, Deserializer,
de::{
DeserializeOwned,
value::{BytesDeserializer, SeqDeserializer, StrDeserializer},
},
};
pub fn deserialize_base64_string<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: DeserializeOwned,
{
let str = <&'de str>::deserialize(deserializer)?;
if str.is_empty() {
Ok(None)
} else {
let bytes = STANDARD
.decode(str.as_bytes())
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
Ok(Some(T::deserialize(BytesDeserializer::new(&bytes))?))
}
}
pub fn deserialize_string_list<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
let str = <&'de str>::deserialize(deserializer)?;
if str.is_empty() {
Ok(Vec::new())
} else {
Ok(Vec::<T>::deserialize(SeqDeserializer::new(str.split(',')))?)
}
}
pub fn deserialize_string_option<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
let str = <&'de str>::deserialize(deserializer)?;
if str.is_empty() {
Ok(None)
} else {
Ok(Some(T::deserialize(StrDeserializer::new(str))?))
}
}
#[macro_export]
macro_rules! mk_option_deserializer {
($(#[$meta:meta])* $vis:vis $name:ident, $repr:ty, $value:ty, $filter_map:expr) => {
$(#[$meta])*
$vis fn $name<'de, D>(d: D) -> core::result::Result<core::option::Option<$value>, D::Error>
where
D: ::serde::Deserializer<'de>,
{
use ::serde::Deserialize;
let v = <$repr>::deserialize(d)?;
Ok($filter_map(v))
}
}
}
crate::mk_option_deserializer!(
pub derp_region_id, u32, crate::DerpRegionId, |id| {
core::num::NonZeroU32::new(id).map(crate::DerpRegionId::from)
});
pub fn is_default<T>(t: &T) -> bool
where
T: Default + PartialEq,
{
t == &T::default()
}