Skip to content
This repository was archived by the owner on Mar 4, 2024. It is now read-only.

Commit 991981e

Browse files
authored
Merge pull request #200 from andy128k/derive-downgrade
Implement derive macro for Downgrade/Upgrade traits
2 parents a0d0926 + ad4c2d0 commit 991981e

File tree

8 files changed

+598
-2
lines changed

8 files changed

+598
-2
lines changed
+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2020, The Gtk-rs Project Developers.
2+
// See the COPYRIGHT file at the top-level directory of this distribution.
3+
// Licensed under the MIT license, see the LICENSE file or <https://opensource.org/licenses/MIT>
4+
5+
use super::fields::{derive_downgrade_fields, DowngradeStructParts};
6+
use proc_macro::TokenStream;
7+
use quote::{format_ident, quote};
8+
use syn::{Generics, Ident};
9+
10+
/// This function derives a weak type for a given strong enum and
11+
/// implementations of `Downgrade` and `Upgrade` traits.
12+
///
13+
/// # Example
14+
///
15+
/// ```rust,ignore
16+
/// #[derive(glib::Downgrade)]
17+
/// enum Choice {
18+
/// This(X, Y),
19+
/// That { x: X, y: Y },
20+
/// }
21+
/// ```
22+
///
23+
/// Here is what will be derived:
24+
///
25+
/// ```rust,ignore
26+
/// enum ChoiceWeak {
27+
/// This(<X as Downgrade>::Weak, <Y as Downgrade>::Weak),
28+
/// That {
29+
/// x: <X as Downgrade>::Weak,
30+
/// y: <Y as Downgrade>::Weak,
31+
/// },
32+
/// }
33+
///
34+
/// impl glib::clone::Downgrade for Choice {
35+
/// type Weak = ChoiceWeak;
36+
///
37+
/// fn downgrade(&self) -> Self::Weak {
38+
/// match self {
39+
/// Self::This(ref _0, ref _1) => Self::Weak::This(
40+
/// glib::clone::Downgrade::downgrade(_0),
41+
/// glib::clone::Downgrade::downgrade(_1),
42+
/// ),
43+
/// Self::That { ref x, ref y } => Self::Weak::That(
44+
/// glib::clone::Downgrade::downgrade(x),
45+
/// glib::clone::Downgrade::downgrade(y),
46+
/// ),
47+
/// }
48+
/// }
49+
/// }
50+
///
51+
/// impl glib::clone::Upgrade for ChoiceWeak {
52+
/// type Strong = Choice;
53+
///
54+
/// fn upgrade(&self) -> Option<Self::Strong> {
55+
/// Some(match self {
56+
/// Self::This(ref _0, ref _1) => Self::Strong::This(
57+
/// glib::clone::Upgrade::upgrade(_0)?,
58+
/// glib::clone::Upgrade::upgrade(_1)?,
59+
/// ),
60+
/// Self::That { ref x, ref y } => Self::Strong::That(
61+
/// glib::clone::Upgrade::upgrade(x)?,
62+
/// glib::clone::Upgrade::upgrade(y)?,
63+
/// ),
64+
/// })
65+
/// }
66+
/// }
67+
/// ```
68+
pub fn derive_downgrade_for_enum(
69+
ident: Ident,
70+
generics: Generics,
71+
data_enum: syn::DataEnum,
72+
) -> TokenStream {
73+
let weak_type = format_ident!("{}Weak", ident);
74+
75+
let variants: Vec<(Ident, DowngradeStructParts)> = data_enum
76+
.variants
77+
.into_iter()
78+
.map(|variant| (variant.ident, derive_downgrade_fields(variant.fields)))
79+
.collect();
80+
81+
let weak_variants: Vec<_> = variants
82+
.iter()
83+
.map(|(ident, parts)| {
84+
let weak_fields = &parts.weak_fields;
85+
quote! {
86+
#ident #weak_fields
87+
}
88+
})
89+
.collect();
90+
91+
let downgrade_variants: Vec<_> = variants
92+
.iter()
93+
.map(|(ident, parts)| {
94+
let destruct = &parts.destruct;
95+
let downgrade = &parts.downgrade;
96+
quote! {
97+
Self::#ident #destruct => Self::Weak::#ident #downgrade
98+
}
99+
})
100+
.collect();
101+
102+
let upgrade_variants: Vec<_> = variants
103+
.iter()
104+
.map(|(ident, parts)| {
105+
let destruct = &parts.destruct;
106+
let upgrade = &parts.upgrade;
107+
quote! {
108+
Self::#ident #destruct => Self::Strong::#ident #upgrade
109+
}
110+
})
111+
.collect();
112+
113+
let derived = quote! {
114+
pub enum #weak_type #generics {#(
115+
#weak_variants
116+
),*}
117+
118+
impl #generics glib::clone::Downgrade for #ident #generics {
119+
type Weak = #weak_type #generics;
120+
121+
fn downgrade(&self) -> Self::Weak {
122+
match self {#(
123+
#downgrade_variants
124+
),*}
125+
}
126+
}
127+
128+
impl #generics glib::clone::Upgrade for #weak_type #generics {
129+
type Strong = #ident #generics;
130+
131+
fn upgrade(&self) -> Option<Self::Strong> {
132+
Some(match self {#(
133+
#upgrade_variants
134+
),*})
135+
}
136+
}
137+
};
138+
139+
derived.into()
140+
}
+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright 2020, The Gtk-rs Project Developers.
2+
// See the COPYRIGHT file at the top-level directory of this distribution.
3+
// Licensed under the MIT license, see the LICENSE file or <https://opensource.org/licenses/MIT>
4+
5+
use proc_macro2::TokenStream;
6+
use quote::{format_ident, quote};
7+
use syn::{Fields, FieldsNamed, FieldsUnnamed, Ident, Type};
8+
9+
/// Parts needed to derive Downgrade and Upgrade implementation.
10+
pub 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+
/// ```
101+
pub fn derive_downgrade_fields(fields: syn::Fields) -> DowngradeStructParts {
102+
match fields {
103+
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
104+
let fields: Vec<Type> = unnamed
105+
.into_pairs()
106+
.map(|pair| pair.into_value())
107+
.map(|field| field.ty)
108+
.collect();
109+
110+
let weak_fields: Vec<_> = fields
111+
.iter()
112+
.map(|ty| {
113+
quote! {
114+
<#ty as glib::clone::Downgrade>::Weak
115+
}
116+
})
117+
.collect();
118+
119+
let field_ident: Vec<Ident> =
120+
(0..fields.len()).map(|i| format_ident!("_{}", i)).collect();
121+
122+
DowngradeStructParts {
123+
weak_fields: quote! {
124+
(#(
125+
#weak_fields
126+
),*)
127+
},
128+
end_of_struct: quote!(;),
129+
destruct: quote! {
130+
(#(
131+
ref #field_ident
132+
),*)
133+
},
134+
downgrade: quote! {
135+
(#(
136+
glib::clone::Downgrade::downgrade(#field_ident)
137+
),*)
138+
},
139+
upgrade: quote! {
140+
(#(
141+
glib::clone::Upgrade::upgrade(#field_ident)?
142+
),*)
143+
},
144+
}
145+
}
146+
Fields::Named(FieldsNamed { named, .. }) => {
147+
let fields: Vec<(Ident, Type)> = named
148+
.into_pairs()
149+
.map(|pair| pair.into_value())
150+
.map(|field| (field.ident.expect("Field ident is specified"), field.ty))
151+
.collect();
152+
153+
let weak_fields: Vec<_> = fields
154+
.iter()
155+
.map(|(ident, ty)| {
156+
quote! {
157+
#ident: <#ty as glib::clone::Downgrade>::Weak
158+
}
159+
})
160+
.collect();
161+
162+
let field_ident: Vec<_> = fields.iter().map(|(ident, _ty)| ident).collect();
163+
164+
DowngradeStructParts {
165+
weak_fields: quote! {
166+
{#(
167+
#weak_fields
168+
),*}
169+
},
170+
end_of_struct: quote!(),
171+
destruct: quote! {
172+
{#(
173+
ref #field_ident
174+
),*}
175+
},
176+
downgrade: quote! {
177+
{#(
178+
#field_ident: glib::clone::Downgrade::downgrade(#field_ident)
179+
),*}
180+
},
181+
upgrade: quote! {
182+
{#(
183+
#field_ident: glib::clone::Upgrade::upgrade(#field_ident)?
184+
),*}
185+
},
186+
}
187+
}
188+
Fields::Unit => DowngradeStructParts {
189+
weak_fields: quote! {},
190+
end_of_struct: quote! { ; },
191+
destruct: quote! {},
192+
downgrade: quote! {},
193+
upgrade: quote! {},
194+
},
195+
}
196+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2020, The Gtk-rs Project Developers.
2+
// See the COPYRIGHT file at the top-level directory of this distribution.
3+
// Licensed under the MIT license, see the LICENSE file or <https://opensource.org/licenses/MIT>
4+
5+
mod enums;
6+
mod fields;
7+
mod structs;
8+
9+
use proc_macro::TokenStream;
10+
use syn::{Data, DeriveInput};
11+
12+
pub fn impl_downgrade(input: DeriveInput) -> TokenStream {
13+
match input.data {
14+
Data::Struct(data_struct) => {
15+
structs::derive_downgrade_for_struct(input.ident, input.generics, data_struct)
16+
}
17+
Data::Enum(data_enum) => {
18+
enums::derive_downgrade_for_enum(input.ident, input.generics, data_enum)
19+
}
20+
Data::Union(..) => {
21+
panic!("#[derive(Downgrade)] is not available for unions.");
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)