1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::{
4 spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Error, Fields, Generics, Ident,
5};
6
7use crate::utils::*;
8
9pub fn expand_derive(ast: DeriveInput) -> Result<TokenStream, Error> {
10 let StructAttributes { signature, .. } = StructAttributes::parse(&ast.attrs)?;
11
12 let zv = zvariant_path();
13 if let Some(signature) = signature {
14 let signature = match signature.as_str() {
15 "dict" => "a{sv}".to_string(),
16 _ => signature,
17 };
18
19 let name = ast.ident;
21 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
22 return Ok(quote! {
23 impl #impl_generics #zv::Type for #name #ty_generics #where_clause {
24 #[inline]
25 fn signature() -> #zv::Signature<'static> {
26 #zv::Signature::from_static_str(#signature).unwrap()
32 }
33 }
34 });
35 }
36
37 match ast.data {
38 Data::Struct(ds) => match ds.fields {
39 Fields::Named(_) if ds.fields.is_empty() => {
40 impl_empty_struct(ast.ident, ast.generics, &zv)
41 }
42 Fields::Named(_) | Fields::Unnamed(_) => {
43 impl_struct(ast.ident, ast.generics, ds.fields, &zv)
44 }
45 Fields::Unit => impl_unit_struct(ast.ident, ast.generics, &zv),
46 },
47 Data::Enum(data) => impl_enum(ast.ident, ast.generics, ast.attrs, data, &zv),
48 _ => Err(Error::new(
49 ast.span(),
50 "only structs and enums supported at the moment",
51 )),
52 }
53 .map(|implementation| {
54 quote! {
55 #[allow(deprecated)]
56 #implementation
57 }
58 })
59}
60
61fn impl_struct(
62 name: Ident,
63 generics: Generics,
64 fields: Fields,
65 zv: &TokenStream,
66) -> Result<TokenStream, Error> {
67 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
68 let signature = signature_for_struct(&fields, zv, false);
69
70 Ok(quote! {
71 impl #impl_generics #zv::Type for #name #ty_generics #where_clause {
72 #[inline]
73 fn signature() -> #zv::Signature<'static> {
74 #signature
75 }
76 }
77 })
78}
79
80fn signature_for_struct(
81 fields: &Fields,
82 zv: &TokenStream,
83 insert_enum_variant: bool,
84) -> TokenStream {
85 let field_types = fields.iter().map(|field| field.ty.to_token_stream());
86 let new_type = match fields {
87 Fields::Named(_) => false,
88 Fields::Unnamed(_) if field_types.len() == 1 => true,
89 Fields::Unnamed(_) => false,
90 Fields::Unit => panic!("signature_for_struct must not be called for unit fields"),
91 };
92 let inner_impl = if new_type {
93 quote! {
94 #(
95 <#field_types as #zv::Type>::signature()
96 )*
97 }
98 } else {
99 quote! {
100 let mut s = <::std::string::String as ::std::convert::From<_>>::from("(");
101 #(
102 s.push_str(<#field_types as #zv::Type>::signature().as_str());
103 )*
104 s.push_str(")");
105
106 #zv::Signature::from_string_unchecked(s)
107 }
108 };
109
110 if insert_enum_variant {
111 quote! {
112 let inner_signature = {
113 #inner_impl
114 };
115 let mut s = <::std::string::String as ::std::convert::From<_>>::from("(");
116 s.push_str(<u32 as #zv::Type>::signature().as_str());
117 s.push_str(inner_signature.as_str());
118 s.push_str(")");
119
120 #zv::Signature::from_string_unchecked(s)
121 }
122 } else {
123 inner_impl
124 }
125}
126
127fn impl_unit_struct(
128 name: Ident,
129 generics: Generics,
130 zv: &TokenStream,
131) -> Result<TokenStream, Error> {
132 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
133
134 Ok(quote! {
135 impl #impl_generics #zv::Type for #name #ty_generics #where_clause {
136 #[inline]
137 fn signature() -> #zv::Signature<'static> {
138 #zv::Signature::from_static_str_unchecked("")
139 }
140 }
141 })
142}
143
144fn impl_empty_struct(
145 name: Ident,
146 generics: Generics,
147 zv: &TokenStream,
148) -> Result<TokenStream, Error> {
149 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
150
151 Ok(quote! {
152 impl #impl_generics #zv::Type for #name #ty_generics #where_clause {
153 #[inline]
154 fn signature() -> #zv::Signature<'static> {
155 #zv::Signature::from_static_str_unchecked("y")
156 }
157 }
158 })
159}
160
161fn impl_enum(
162 name: Ident,
163 generics: Generics,
164 attrs: Vec<Attribute>,
165 data: DataEnum,
166 zv: &TokenStream,
167) -> Result<TokenStream, Error> {
168 let mut all_signatures: Vec<Result<TokenStream, Error>> = data
169 .variants
170 .iter()
171 .map(|variant| signature_for_variant(variant, &attrs, zv))
172 .collect();
173 let signature = all_signatures.pop().unwrap()?;
174 for sig in all_signatures {
176 if sig?.to_string() != signature.to_string() {
177 return Err(Error::new(
178 name.span(),
179 "all variants must have the same number and type of fields",
180 ));
181 }
182 }
183
184 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
185
186 Ok(quote! {
187 impl #impl_generics #zv::Type for #name #ty_generics #where_clause {
188 #[inline]
189 fn signature() -> #zv::Signature<'static> {
190 #signature
191 }
192 }
193 })
194}
195
196fn signature_for_variant(
197 variant: &syn::Variant,
198 attrs: &[Attribute],
199 zv: &TokenStream,
200) -> Result<TokenStream, Error> {
201 let repr = attrs.iter().find(|attr| attr.path.is_ident("repr"));
202 match &variant.fields {
203 Fields::Unit => {
204 let repr = match repr {
205 Some(repr_attr) => repr_attr.parse_args()?,
206 None => quote! { u32 },
207 };
208
209 Ok(quote! { <#repr as #zv::Type>::signature() })
210 }
211 Fields::Named(_) => Ok(signature_for_struct(&variant.fields, zv, true)),
212 Fields::Unnamed(_) => Ok(signature_for_struct(&variant.fields, zv, true)),
213 }
214}