Skip to main content

zvariant_derive/
dict.rs

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
19/// Whether the dict's value type is `Variant` (i.e. signature `a{?v}`).
20///
21/// Variant-typed values get wrapped/unwrapped via `as_value`; any other value type defers to
22/// the field type's own `Serialize`/`Deserialize`.
23fn 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
43/// Implements `Serialize` for structs as D-Bus dictionaries via a serde helper.
44pub 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
154/// Implements `Deserialize` for structs from D-Bus dictionaries via a serde helper.
155pub 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    // Create a new generics with a 'de lifetime
169    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}