time_macros/
serde_format_description.rs

1use proc_macro::{Ident, TokenStream, TokenTree};
2
3pub(crate) fn build(
4    visibility: TokenStream,
5    mod_name: Ident,
6    ty: TokenTree,
7    format: TokenStream,
8    format_description_display: String,
9) -> TokenStream {
10    let ty_s = &*ty.to_string();
11
12    let visitor = if cfg!(feature = "parsing") {
13        quote! {
14            struct Visitor;
15            struct OptionVisitor;
16
17            impl<'a> ::serde::de::Visitor<'a> for Visitor {
18                type Value = __TimeSerdeType;
19
20                fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
21                    write!(
22                        f,
23                        concat!(
24                            "a(n) `",
25                            #(ty_s),
26                            "` in the format \"{}\"",
27                        ),
28                        #(format_description_display.as_str())
29                    )
30                }
31
32                fn visit_str<E: ::serde::de::Error>(
33                    self,
34                    value: &str
35                ) -> Result<__TimeSerdeType, E> {
36                    __TimeSerdeType::parse(value, &description()).map_err(E::custom)
37                }
38            }
39
40            impl<'a> ::serde::de::Visitor<'a> for OptionVisitor {
41                type Value = Option<__TimeSerdeType>;
42
43                fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
44                    write!(
45                        f,
46                        concat!(
47                            "an `Option<",
48                            #(ty_s),
49                            ">` in the format \"{}\"",
50                        ),
51                        #(format_description_display.as_str())
52                    )
53                }
54
55                fn visit_some<D: ::serde::de::Deserializer<'a>>(
56                    self,
57                    deserializer: D
58                ) -> Result<Option<__TimeSerdeType>, D::Error> {
59                    deserializer
60                        .deserialize_str(Visitor)
61                        .map(Some)
62                }
63
64                fn visit_none<E: ::serde::de::Error>(
65                    self
66                ) -> Result<Option<__TimeSerdeType>, E> {
67                    Ok(None)
68                }
69            }
70        }
71    } else {
72        quote!()
73    };
74
75    let serialize_primary = if cfg!(feature = "formatting") {
76        quote! {
77            pub fn serialize<S: ::serde::Serializer>(
78                datetime: &__TimeSerdeType,
79                serializer: S,
80            ) -> Result<S::Ok, S::Error> {
81                use ::serde::Serialize;
82                datetime
83                    .format(&description())
84                    .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
85                    .serialize(serializer)
86            }
87        }
88    } else {
89        quote!()
90    };
91
92    let deserialize_primary = if cfg!(feature = "parsing") {
93        quote! {
94            pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
95                deserializer: D
96            ) -> Result<__TimeSerdeType, D::Error> {
97                use ::serde::Deserialize;
98                deserializer.deserialize_str(Visitor)
99            }
100        }
101    } else {
102        quote!()
103    };
104
105    let serialize_option = if cfg!(feature = "formatting") {
106        quote! {
107            #[allow(clippy::ref_option)]
108            pub fn serialize<S: ::serde::Serializer>(
109                option: &Option<__TimeSerdeType>,
110                serializer: S,
111            ) -> Result<S::Ok, S::Error> {
112                use ::serde::Serialize;
113                option.map(|datetime| datetime.format(&description()))
114                    .transpose()
115                    .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
116                    .serialize(serializer)
117            }
118        }
119    } else {
120        quote!()
121    };
122
123    let deserialize_option = if cfg!(feature = "parsing") {
124        quote! {
125            pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
126                deserializer: D
127            ) -> Result<Option<__TimeSerdeType>, D::Error> {
128                use ::serde::Deserialize;
129                deserializer.deserialize_option(OptionVisitor)
130            }
131        }
132    } else {
133        quote!()
134    };
135
136    let deserialize_option_imports = if cfg!(feature = "parsing") {
137        quote! {
138            use super::{OptionVisitor, Visitor};
139        }
140    } else {
141        quote!()
142    };
143
144    let fd_traits = match (cfg!(feature = "formatting"), cfg!(feature = "parsing")) {
145        (false, false) => {
146            bug!("serde_format_description::build called without formatting or parsing enabled")
147        }
148        (false, true) => quote! { ::time::parsing::Parsable },
149        (true, false) => quote! { ::time::formatting::Formattable },
150        (true, true) => quote! { ::time::formatting::Formattable + ::time::parsing::Parsable },
151    };
152
153    quote! {
154        #S(visibility) mod #(mod_name) {
155            use super::*;
156            // TODO Remove the prefix, forcing the user to import the type themself. This must be
157            // done in a breaking change.
158            use ::time::#(ty) as __TimeSerdeType;
159
160            const fn description() -> impl #S(fd_traits) {
161                #S(format)
162            }
163
164            #S(visitor)
165            #S(serialize_primary)
166            #S(deserialize_primary)
167
168            // While technically public, this is effectively the same visibility as the enclosing
169            // module, which has its visibility controlled by the user.
170            pub mod option {
171                use super::{description, __TimeSerdeType};
172                #S(deserialize_option_imports)
173
174                #S(serialize_option)
175                #S(deserialize_option)
176            }
177        }
178    }
179}