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