use proc_macro::{Ident, TokenStream, TokenTree};
pub(crate) fn build(
mod_name: Ident,
ty: TokenTree,
format: TokenStream,
format_description_display: String,
) -> TokenStream {
let ty_s = &*ty.to_string();
let visitor = if cfg!(feature = "parsing") {
quote! {
struct Visitor;
struct OptionVisitor;
impl<'a> ::serde::de::Visitor<'a> for Visitor {
type Value = __TimeSerdeType;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"a(n) `",
#(ty_s),
"` in the format \"{}\"",
),
#(format_description_display.as_str())
)
}
fn visit_str<E: ::serde::de::Error>(
self,
value: &str
) -> Result<__TimeSerdeType, E> {
__TimeSerdeType::parse(value, &description()).map_err(E::custom)
}
}
impl<'a> ::serde::de::Visitor<'a> for OptionVisitor {
type Value = Option<__TimeSerdeType>;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"an `Option<",
#(ty_s),
">` in the format \"{}\"",
),
#(format_description_display.as_str())
)
}
fn visit_some<D: ::serde::de::Deserializer<'a>>(
self,
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
deserializer
.deserialize_str(Visitor)
.map(Some)
}
fn visit_none<E: ::serde::de::Error>(
self
) -> Result<Option<__TimeSerdeType>, E> {
Ok(None)
}
}
}
} else {
quote!()
};
let serialize_primary = if cfg!(feature = "formatting") {
quote! {
pub fn serialize<S: ::serde::Serializer>(
datetime: &__TimeSerdeType,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
datetime
.format(&description())
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
}
} else {
quote!()
};
let deserialize_primary = if cfg!(feature = "parsing") {
quote! {
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<__TimeSerdeType, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_str(Visitor)
}
}
} else {
quote!()
};
let serialize_option = if cfg!(feature = "formatting") {
quote! {
pub fn serialize<S: ::serde::Serializer>(
option: &Option<__TimeSerdeType>,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
option.map(|datetime| datetime.format(&description()))
.transpose()
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
}
} else {
quote!()
};
let deserialize_option = if cfg!(feature = "parsing") {
quote! {
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_option(OptionVisitor)
}
}
} else {
quote!()
};
let deserialize_option_imports = if cfg!(feature = "parsing") {
quote! {
use super::{OptionVisitor, Visitor};
}
} else {
quote!()
};
let fd_traits = match (cfg!(feature = "formatting"), cfg!(feature = "parsing")) {
(false, false) => {
bug!("serde_format_description::build called without formatting or parsing enabled")
}
(false, true) => quote! { ::time::parsing::Parsable },
(true, false) => quote! { ::time::formatting::Formattable },
(true, true) => quote! { ::time::formatting::Formattable + ::time::parsing::Parsable },
};
quote! {
mod #(mod_name) {
use ::time::#(ty) as __TimeSerdeType;
const fn description() -> impl #S(fd_traits) {
#S(format)
}
#S(visitor)
#S(serialize_primary)
#S(deserialize_primary)
pub(super) mod option {
use super::{description, __TimeSerdeType};
#S(deserialize_option_imports)
#S(serialize_option)
#S(deserialize_option)
}
}
}
}