1use std::str::FromStr;
2
3use proc_macro2::{Span, TokenStream};
4use quote::{format_ident, quote};
5use syn::{Data, DeriveInput, Error, Field, punctuated::Punctuated, spanned::Spanned};
6
7use super::{Config, attrs::*};
8use crate::{macros, signature::Signature};
9
10fn dict_name_for_field(
11 f: &Field,
12 rename_attr: Option<String>,
13 rename_all_attr: Option<&str>,
14) -> Result<String, Error> {
15 let ident = f.ident.as_ref().unwrap().to_string();
16 rename_identifier(ident, f.span(), rename_attr, rename_all_attr)
17}
18
19fn dict_value_is_variant(signature: Option<&str>, span: Span) -> Result<bool, Error> {
24 let Some(s) = signature else {
25 return Ok(true);
26 };
27 if s == "dict" {
28 return Ok(true);
29 }
30 let sig = Signature::from_str(s).map_err(|e| Error::new(span, e))?;
31 match sig {
32 Signature::Dict { value, .. } => match &*value {
33 Signature::Variant => Ok(true),
34 _ => Ok(false),
35 },
36 _ => Err(Error::new(
37 span,
38 "`*Dict` derive requires a dictionary signature (e.g. `a{sv}` or `a{sa{sv}}`)",
39 )),
40 }
41}
42
43pub fn expand_serialize_dict_derive(
45 input: DeriveInput,
46 config: &Config,
47) -> Result<TokenStream, Error> {
48 let StructAttributes {
49 signature,
50 rename_all,
51 crate_path: crate_attr,
52 ..
53 } = StructAttributes::parse_with_lists(&input.attrs, config.attr_lists)?;
54 let value_is_variant = dict_value_is_variant(signature.as_deref(), input.span())?;
55 let rename_all_str = rename_all.as_deref().unwrap_or("snake_case");
56 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
57 let name = &input.ident;
58 let helper = format_ident!("__SerializeDict{}", name);
59 let zv = config.resolve_path(crate_attr.as_deref())?;
60
61 let mut field_defs = Vec::new();
62 let mut field_inits = Vec::new();
63 let Data::Struct(data) = &input.data else {
64 return Err(Error::new(input.span(), "only structs supported"));
65 };
66 for field in &data.fields {
67 let ident = field.ident.as_ref().unwrap();
68 let ty = &field.ty;
69 let FieldAttributes { rename, .. } =
70 FieldAttributes::parse_with_lists(&field.attrs, config.attr_lists)?;
71 let dict_name = dict_name_for_field(field, rename, rename_all.as_deref())?;
72 let is_opt = macros::ty_is_option(ty);
73 let field_def = match (value_is_variant, is_opt) {
74 (true, true) => {
75 let path = format!("{}::as_value::optional", quote! { #zv });
76 quote! {
77 #[serde(
78 rename = #dict_name,
79 with = #path,
80 skip_serializing_if = "Option::is_none",
81 )]
82 #ident: &'a #ty
83 }
84 }
85 (true, false) => {
86 let path = format!("{}::as_value", quote! { #zv });
87 quote! {
88 #[serde(rename = #dict_name, with = #path)]
89 #ident: &'a #ty
90 }
91 }
92 (false, true) => quote! {
93 #[serde(
94 rename = #dict_name,
95 serialize_with = "__zv_dict_ser_opt",
96 skip_serializing_if = "Option::is_none",
97 )]
98 #ident: &'a #ty
99 },
100 (false, false) => quote! {
101 #[serde(rename = #dict_name)]
102 #ident: &'a #ty
103 },
104 };
105 field_defs.push(field_def);
106 field_inits.push(quote! { #ident: &self.#ident });
107 }
108
109 let opt_serializer = (!value_is_variant).then(|| {
110 quote! {
111 fn __zv_dict_ser_opt<T, S>(
112 value: &::std::option::Option<T>,
113 serializer: S,
114 ) -> ::std::result::Result<S::Ok, S::Error>
115 where
116 T: #zv::export::serde::Serialize,
117 S: #zv::export::serde::Serializer,
118 {
119 <T as #zv::export::serde::Serialize>::serialize(
120 value.as_ref().unwrap(),
121 serializer,
122 )
123 }
124 }
125 });
126
127 Ok(quote! {
128 #[allow(deprecated)]
129 impl #impl_generics #zv::export::serde::ser::Serialize for #name #ty_generics #where_clause {
130 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
131 where
132 S: #zv::export::serde::ser::Serializer,
133 {
134 use #zv::export::serde::Serialize;
135
136 #opt_serializer
137
138 #[derive(Serialize)]
139 #[serde(rename_all = #rename_all_str)]
140 struct #helper<'a> {
141 #[serde(skip)]
142 phantom: ::std::marker::PhantomData<&'a ()>,
143 #(#field_defs,)*
144 }
145
146 let helper = #helper {
147 phantom: ::std::marker::PhantomData,
148 #(#field_inits,)*
149 };
150
151 helper.serialize(serializer)
152 }
153 }
154 })
155}
156
157pub fn expand_deserialize_dict_derive(
159 input: DeriveInput,
160 config: &Config,
161) -> Result<TokenStream, Error> {
162 let StructAttributes {
163 signature,
164 rename_all,
165 deny_unknown_fields,
166 crate_path: crate_attr,
167 ..
168 } = StructAttributes::parse_with_lists(&input.attrs, config.attr_lists)?;
169 let value_is_variant = dict_value_is_variant(signature.as_deref(), input.span())?;
170 let rename_all_str = rename_all.as_deref().unwrap_or("snake_case");
171 let zv = config.resolve_path(crate_attr.as_deref())?;
172
173 let mut generics = input.generics.clone();
175 let lifetime_param = syn::LifetimeParam {
176 attrs: Vec::new(),
177 lifetime: syn::Lifetime::new("'de", Span::call_site()),
178 colon_token: None,
179 bounds: Punctuated::new(),
180 };
181 generics
182 .params
183 .insert(0, syn::GenericParam::Lifetime(lifetime_param));
184
185 let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
186 let (_, orig_ty_generics, _) = input.generics.split_for_impl();
187 let name = &input.ident;
188 let helper = format_ident!("__DeserializeDict{}", name);
189
190 let mut field_defs = Vec::new();
191 let mut field_assignments = Vec::new();
192 let mut non_optional_field_checks = Vec::new();
193 let Data::Struct(data) = &input.data else {
194 return Err(Error::new(input.span(), "only structs supported"));
195 };
196 let opt_path = if value_is_variant {
197 format!("{}::as_value::optional", quote! { #zv })
198 } else {
199 "__zv_dict_de_opt".to_string()
200 };
201 for field in &data.fields {
202 let ident = field.ident.as_ref().unwrap();
203 let ty = &field.ty;
204 let FieldAttributes { rename, .. } =
205 FieldAttributes::parse_with_lists(&field.attrs, config.attr_lists)?;
206 let dict_name = dict_name_for_field(field, rename, rename_all.as_deref())?;
207 let is_opt = macros::ty_is_option(ty);
208
209 let with_attr = if value_is_variant {
210 quote! { with = #opt_path }
211 } else {
212 quote! { deserialize_with = #opt_path }
213 };
214
215 if is_opt {
216 field_defs.push(quote! {
217 #[serde(rename = #dict_name, #with_attr, default)]
218 #ident: #ty
219 });
220 field_assignments.push(quote! { #ident: helper.#ident });
221 } else {
222 field_defs.push(quote! {
223 #[serde(rename = #dict_name, #with_attr, default)]
224 #ident: ::std::option::Option<#ty>
225 });
226
227 non_optional_field_checks.push(quote! {
228 if helper.#ident.is_none() {
229 return ::std::result::Result::Err(
230 <D::Error as #zv::export::serde::de::Error>::missing_field(#dict_name),
231 );
232 }
233 });
234
235 field_assignments.push(quote! { #ident: helper.#ident.unwrap() });
236 }
237 }
238
239 let deny_attr = if deny_unknown_fields {
240 quote! { , deny_unknown_fields }
241 } else {
242 quote! {}
243 };
244
245 let opt_deserializer = (!value_is_variant).then(|| {
246 quote! {
247 fn __zv_dict_de_opt<'de, T, D>(
248 deserializer: D,
249 ) -> ::std::result::Result<::std::option::Option<T>, D::Error>
250 where
251 T: #zv::export::serde::Deserialize<'de>,
252 D: #zv::export::serde::Deserializer<'de>,
253 {
254 <T as #zv::export::serde::Deserialize<'de>>::deserialize(deserializer)
255 .map(::std::option::Option::Some)
256 }
257 }
258 });
259
260 Ok(quote! {
261 #[allow(deprecated)]
262 impl #impl_generics #zv::export::serde::de::Deserialize<'de> for #name #orig_ty_generics
263 #where_clause
264 {
265 fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
266 where
267 D: #zv::export::serde::de::Deserializer<'de>,
268 {
269 use #zv::export::serde::Deserialize;
270
271 #opt_deserializer
272
273 #[derive(Deserialize, Default)]
274 #[serde(default, rename_all = #rename_all_str #deny_attr)]
275 struct #helper {
276 #(#field_defs,)*
277 }
278
279 let helper = #helper::deserialize(deserializer)?;
280
281 #(#non_optional_field_checks)*
282
283 Ok(Self {
284 #(#field_assignments,)*
285 })
286 }
287 }
288 })
289}