use serde::Deserialize;
fn present<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de>,
{
struct Visitor<T>(std::marker::PhantomData<T>);
impl<'de, T: Deserialize<'de>> serde::de::Visitor<'de> for Visitor<T> {
type Value = Option<T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a stated value, or the key left out")
}
fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Err(E::custom(
"this key states a value or is left out; it does not state null",
))
}
fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Err(E::custom(
"this key states a value or is left out; it does not state null",
))
}
fn visit_some<D: serde::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
T::deserialize(deserializer).map(Some)
}
}
deserializer.deserialize_option(Visitor(std::marker::PhantomData))
}
pub fn named_present<'de, D, T>(deserializer: D, field: &str) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de>,
{
present(deserializer)
.map_err(|error| serde::de::Error::custom(format_args!("{field}: {error}")))
}
#[macro_export]
macro_rules! named_optional_field {
($vis:vis $name:ident, $value:ty, $field:literal) => {
$vis fn $name<'de, D: ::serde::Deserializer<'de>>(
deserializer: D,
) -> ::core::result::Result<::core::option::Option<$value>, D::Error> {
$crate::absent_key::named_present(deserializer, $field)
}
};
($vis:vis $name:ident<$($parameter:ident),+ $(,)?>, $value:ty, $field:literal) => {
$vis fn $name<'de, D, $($parameter),+>(
deserializer: D,
) -> ::core::result::Result<::core::option::Option<$value>, D::Error>
where
D: ::serde::Deserializer<'de>,
$value: ::serde::Deserialize<'de>,
{
$crate::absent_key::named_present(deserializer, $field)
}
};
}
pub fn nullable<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<T>::deserialize(deserializer)
}