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