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( 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>( self, deserializer: D ) -> Result, D::Error> { deserializer .deserialize_str(Visitor) .map(Some) } fn visit_none( self ) -> Result, E> { Ok(None) } } } } else { quote!() }; let serialize_primary = if cfg!(feature = "formatting") { quote! { pub fn serialize( datetime: &__TimeSerdeType, serializer: S, ) -> Result { use ::serde::Serialize; datetime .format(&description()) .map_err(::time::error::Format::into_invalid_serde_value::)? .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( option: &Option<__TimeSerdeType>, serializer: S, ) -> Result { use ::serde::Serialize; option.map(|datetime| datetime.format(&description())) .transpose() .map_err(::time::error::Format::into_invalid_serde_value::)? .serialize(serializer) } } } else { quote!() }; let deserialize_option = if cfg!(feature = "parsing") { quote! { pub fn deserialize<'a, D: ::serde::Deserializer<'a>>( deserializer: D ) -> Result, 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) } } } }