1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use proc_macro2::TokenStream;
4 use quote::{format_ident, quote};
5 use syn::{Fields, FieldsNamed, FieldsUnnamed, Ident, Type};
6 
7 /// Parts needed to derive Downgrade and Upgrade implementation.
8 pub struct DowngradeStructParts {
9     /// Inner part of weak type declaration
10     pub weak_fields: TokenStream,
11     /// Term needed to finish declaration. It is usually blank but is `;` for tuple structs.
12     pub end_of_struct: TokenStream,
13     /// Destructuring pattern
14     pub destruct: TokenStream,
15     /// Downgrade code
16     pub downgrade: TokenStream,
17     /// Upgrade code
18     pub upgrade: TokenStream,
19 }
20 
21 /// This function generates parts needed to derive Downgrade and Upgrade
22 /// implementations.
23 ///
24 /// # Example
25 ///
26 /// Let's assume following types are declared.
27 ///
28 /// ```rust,ignore
29 /// struct Unnamed(X, Y);
30 ///
31 /// struct Named {
32 ///     x: X,
33 ///     y: Y,
34 /// }
35 ///
36 /// enum Choice {
37 ///     This(X, Y),
38 ///     That { x: X, y: Y },
39 /// }
40 /// ```
41 ///
42 /// ## weak_fields
43 ///
44 /// For the struct `Unnamed` and for a enum's variant `Choice::This`
45 /// it will be `(<X as Downgrade>::Weak, <Y as Downgrade>::Weak)`.
46 /// For the struct `Named` and for a enum's variant `Choice::That`
47 /// it will be `{ x: <X as Downgrade>::Weak, y: <Y as Downgrade>::Weak, }`.
48 ///
49 /// ## end_of_struct
50 ///
51 /// It is a semicolon (`;`) for an `Unnamed` and is blank for the rest.
52 ///
53 /// ## destruct
54 ///
55 /// For the struct `Unnamed` and for a enum's variant `Choice::This`
56 /// it will be `(ref _0, ref _1)`.
57 /// For the struct `Named` and for a enum's variant `Choice::That`
58 /// it will be `{ ref x, ref y }`.
59 /// So it can be used as a destructuring pattern for values of both types,
60 /// strong and weak.
61 ///
62 /// ```rust,ignore
63 /// let Unnamed (ref _0, ref _1) = <expression>;
64 /// let Named { ref x, ref y } = <expression>;
65 ///
66 /// match <expression> {
67 ///     Choise::This (ref _0, ref _1) => ... ,
68 ///     Choise::That { ref x, ref y } => ... ,
69 /// }
70 /// ```
71 ///
72 /// # downgrade
73 ///
74 /// ```rust,ignore
75 /// (
76 ///     glib::clone::Downgrade::downgrade(_0),
77 ///     glib::clone::Downgrade::downgrade(_1),
78 /// )
79 ///
80 /// {
81 ///     x: glib::clone::Downgrade::downgrade(x),
82 ///     y: glib::clone::Downgrade::downgrade(y),
83 /// }
84 /// ```
85 ///
86 /// # upgrade
87 ///
88 /// ```rust,ignore
89 /// (
90 ///     glib::clone::Upgrade::upgrade(_0)?,
91 ///     glib::clone::Upgrade::upgrade(_1)?,
92 /// )
93 ///
94 /// {
95 ///     x: glib::clone::Upgrade::upgrade(x)?,
96 ///     y: glib::clone::Upgrade::upgrade(y)?,
97 /// }
98 /// ```
derive_downgrade_fields(fields: syn::Fields) -> DowngradeStructParts99 pub fn derive_downgrade_fields(fields: syn::Fields) -> DowngradeStructParts {
100     match fields {
101         Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
102             let fields: Vec<Type> = unnamed
103                 .into_pairs()
104                 .map(|pair| pair.into_value())
105                 .map(|field| field.ty)
106                 .collect();
107 
108             let weak_fields: Vec<_> = fields
109                 .iter()
110                 .map(|ty| {
111                     quote! {
112                         <#ty as glib::clone::Downgrade>::Weak
113                     }
114                 })
115                 .collect();
116 
117             let field_ident: Vec<Ident> =
118                 (0..fields.len()).map(|i| format_ident!("_{}", i)).collect();
119 
120             DowngradeStructParts {
121                 weak_fields: quote! {
122                     (#(
123                         #weak_fields
124                     ),*)
125                 },
126                 end_of_struct: quote!(;),
127                 destruct: quote! {
128                     (#(
129                         ref #field_ident
130                     ),*)
131                 },
132                 downgrade: quote! {
133                     (#(
134                         glib::clone::Downgrade::downgrade(#field_ident)
135                     ),*)
136                 },
137                 upgrade: quote! {
138                     (#(
139                         glib::clone::Upgrade::upgrade(#field_ident)?
140                     ),*)
141                 },
142             }
143         }
144         Fields::Named(FieldsNamed { named, .. }) => {
145             let fields: Vec<(Ident, Type)> = named
146                 .into_pairs()
147                 .map(|pair| pair.into_value())
148                 .map(|field| (field.ident.expect("Field ident is specified"), field.ty))
149                 .collect();
150 
151             let weak_fields: Vec<_> = fields
152                 .iter()
153                 .map(|(ident, ty)| {
154                     quote! {
155                         #ident: <#ty as glib::clone::Downgrade>::Weak
156                     }
157                 })
158                 .collect();
159 
160             let field_ident: Vec<_> = fields.iter().map(|(ident, _ty)| ident).collect();
161 
162             DowngradeStructParts {
163                 weak_fields: quote! {
164                     {#(
165                         #weak_fields
166                     ),*}
167                 },
168                 end_of_struct: quote!(),
169                 destruct: quote! {
170                     {#(
171                         ref #field_ident
172                     ),*}
173                 },
174                 downgrade: quote! {
175                     {#(
176                         #field_ident: glib::clone::Downgrade::downgrade(#field_ident)
177                     ),*}
178                 },
179                 upgrade: quote! {
180                     {#(
181                         #field_ident: glib::clone::Upgrade::upgrade(#field_ident)?
182                     ),*}
183                 },
184             }
185         }
186         Fields::Unit => DowngradeStructParts {
187             weak_fields: quote! {},
188             end_of_struct: quote! { ; },
189             destruct: quote! {},
190             downgrade: quote! {},
191             upgrade: quote! {},
192         },
193     }
194 }
195