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