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 }
74 } else {
75 quote_!()
76 };
77
78 let serialize_primary = if cfg!(feature = "formatting") {
79 quote_! {
80 pub fn serialize<S: ::serde::Serializer>(
81 datetime: &__TimeSerdeType,
82 serializer: S,
83 ) -> Result<S::Ok, S::Error> {
84 use ::serde::Serialize;
85 datetime
86 .format(&description())
87 .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
88 .serialize(serializer)
89 }
90 }
91 } else {
92 quote_!()
93 };
94
95 let deserialize_primary = if cfg!(feature = "parsing") {
96 quote_! {
97 pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
98 deserializer: D
99 ) -> Result<__TimeSerdeType, D::Error> {
100 use ::serde::Deserialize;
101 deserializer.deserialize_str(Visitor)
102 }
103 }
104 } else {
105 quote_!()
106 };
107
108 let serialize_option = if cfg!(feature = "formatting") {
109 quote_! {
110 #[expect(clippy::ref_option)]
111 pub fn serialize<S: ::serde::Serializer>(
112 option: &Option<__TimeSerdeType>,
113 serializer: S,
114 ) -> Result<S::Ok, S::Error> {
115 use ::serde::Serialize;
116 option.map(|datetime| datetime.format(&description()))
117 .transpose()
118 .map_err(::time::error::Format::into_invalid_serde_value::<S>)?
119 .serialize(serializer)
120 }
121 }
122 } else {
123 quote_!()
124 };
125
126 let deserialize_option = if cfg!(feature = "parsing") {
127 quote_! {
128 pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
129 deserializer: D
130 ) -> Result<Option<__TimeSerdeType>, D::Error> {
131 use ::serde::Deserialize;
132 deserializer.deserialize_option(OptionVisitor)
133 }
134 }
135 } else {
136 quote_!()
137 };
138
139 let deserialize_option_imports = if cfg!(feature = "parsing") {
140 quote_! {
141 use super::__hygiene::{OptionVisitor, Visitor};
142 }
143 } else {
144 quote_!()
145 };
146
147 let fd_traits = match (cfg!(feature = "formatting"), cfg!(feature = "parsing")) {
148 (false, false) => {
149 bug!("serde_format_description::build called without formatting or parsing enabled")
150 }
151 (false, true) => quote_! { ::time::parsing::Parsable },
152 (true, false) => quote_! { ::time::formatting::Formattable },
153 (true, true) => quote_! { ::time::formatting::Formattable + ::time::parsing::Parsable },
154 };
155
156 let hygiene_imports = match (cfg!(feature = "formatting"), cfg!(feature = "parsing")) {
157 (false, false) => bug!(
158 "`serde_format_description` cannot be enabled without either the `formatting` or \
159 `parsing` features"
160 ),
161 (false, true) => quote_! { pub use self::__hygiene::deserialize; },
162 (true, false) => quote_! { pub use self::__hygiene::serialize; },
163 (true, true) => quote_! { pub use self::__hygiene::{serialize, deserialize}; },
164 };
165
166 let main_type_import = if version.is_at_most_v2() {
167 quote_! { use ::time::#S(ty) as __TimeSerdeType; }
168 } else {
169 quote_! { use #S(ty) as __TimeSerdeType; }
170 };
171
172 quote_! {
173 #S(visibility) mod #(mod_name) {
174 use super::*;
175 #S(main_type_import)
176 #[expect(clippy::pub_use)]
177 #S(hygiene_imports)
178
179 const fn description() -> impl #S(fd_traits) {
180 #S(format)
181 }
182
183 mod __hygiene {
184 use super::{description, __TimeSerdeType};
185
186 #S(visitor)
187 #S(serialize_primary)
188 #S(deserialize_primary)
189 }
190
191 pub mod option {
194 use super::{description, __TimeSerdeType};
195 #S(deserialize_option_imports)
196
197 #S(serialize_option)
198 #S(deserialize_option)
199 }
200 }
201 }
202}