time_macros/
serde_format_description.rs

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