relm_derive_common/
lib.rs1extern crate proc_macro2;
2#[macro_use]
3extern crate quote;
4extern crate relm_gen_widget;
5extern crate syn;
6
7use proc_macro2::TokenStream;
8use relm_gen_widget::gen_where_clause;
9use syn::{
10 Fields,
11 GenericParam,
12 Generics,
13 Ident,
14 Item,
15 LifetimeDef,
16 TypeParam,
17};
18
19pub fn impl_msg(ast: &Item, krate: Ident) -> TokenStream {
20 let display = derive_display_variant(ast, &krate);
21 let into_option = derive_into_option(ast, &krate);
22
23 quote! {
24 #display
25 #into_option
26 }
27}
28
29pub fn impl_simple_msg(ast: &Item, krate: Ident) -> TokenStream {
30 if let Item::Enum(ref enum_item) = *ast {
31 let name = &enum_item.ident;
32
33 let display = derive_display_variant(ast, &krate);
34 let into_option = derive_into_option(ast, &krate);
35 let match_clone = derive_partial_clone(ast);
36
37 let generics = &enum_item.generics;
38 let generics_without_bound = remove_generic_bounds(generics);
39 let typ = quote! {
40 #name #generics_without_bound
41 };
42 let where_clause = gen_where_clause(generics);
43
44 quote! {
45 #display
46 #into_option
47
48 impl #generics FnOnce<((),)> for #typ #where_clause {
49 type Output = #typ;
50
51 extern "rust-call" fn call_once(self, args: ((),)) -> Self::Output {
52 self.call(args)
53 }
54 }
55
56 impl #generics FnMut<((),)> for #typ #where_clause {
57 extern "rust-call" fn call_mut(&mut self, args: ((),)) -> Self::Output {
58 self.call(args)
59 }
60 }
61
62 impl #generics Fn<((),)> for #typ #where_clause {
63 extern "rust-call" fn call(&self, _: ((),)) -> Self::Output {
64 #match_clone
65 }
66 }
67 }
68 }
69 else {
70 panic!("expected enum");
71 }
72}
73
74fn derive_partial_clone(ast: &Item) -> TokenStream {
75 if let Item::Enum(ref enum_item) = *ast {
76 let name = &enum_item.ident;
77 let mut patterns = vec![];
78 let mut values = vec![];
79 for variant in &enum_item.variants {
80 if variant.fields == Fields::Unit {
81 let ident = &variant.ident;
82 patterns.push(quote! {
83 #name::#ident
84 });
85 values.push(&variant.ident);
86 }
87 }
88 quote! {
89 #[allow(unreachable_patterns)]
90 match *self {
91 #(#patterns => #values,)*
92 _ => panic!("Expected a variant without parameter"),
93 }
94 }
95 }
96 else {
97 panic!("Expected enum but found {:?}", ast);
98 }
99}
100
101fn derive_display_variant(ast: &Item, krate: &Ident) -> TokenStream {
102 if let Item::Enum(ref enum_item) = *ast {
103 let generics = &enum_item.generics;
104 let name = &enum_item.ident;
105 let generics_without_bound = remove_generic_bounds(generics);
106 let typ = quote! {
107 #name #generics_without_bound
108 };
109
110 let variant_patterns = enum_item.variants.iter().map(|variant| {
111 let attrs = &variant.attrs;
112 let ident = &variant.ident;
113 quote! {
114 #(#attrs)* #name::#ident { .. }
115 }
116 });
117 let variant_names = enum_item.variants.iter().map(|variant| {
118 variant.ident.to_string()
119 });
120 let where_clause = gen_where_clause(generics);
121
122 quote_spanned! { krate.span() =>
123 impl #generics ::#krate::DisplayVariant for #typ #where_clause {
124 #[allow(unused_qualifications)]
125 fn display_variant(&self) -> &'static str {
126 match *self {
127 #(#variant_patterns => #variant_names,)*
128 }
129 }
130 }
131 }
132 }
133 else {
134 panic!("Expected enum");
135 }
136}
137
138fn derive_into_option(ast: &Item, krate: &Ident) -> TokenStream {
139 if let Item::Enum(ref enum_item) = *ast {
140 let generics = &enum_item.generics;
141 let name = &enum_item.ident;
142 let generics_without_bound = remove_generic_bounds(generics);
143 let typ = quote! {
144 #name #generics_without_bound
145 };
146 let where_clause = gen_where_clause(generics);
147
148 quote_spanned! { krate.span() =>
149 impl #generics ::#krate::IntoOption<#typ> for #typ #where_clause {
150 fn into_option(self) -> Option<#typ> {
151 Some(self)
152 }
153 }
154 }
155 }
156 else {
157 panic!("Expecting enum");
158 }
159}
160
161fn remove_generic_bounds(generics: &Generics) -> Generics {
162 let mut generics = generics.clone();
163 for param in generics.params.iter_mut() {
164 match *param {
165 GenericParam::Lifetime(LifetimeDef { ref mut bounds, .. }) =>
166 while bounds.pop().is_some() {
167 },
168 GenericParam::Type(TypeParam { ref mut bounds, .. }) =>
169 while bounds.pop().is_some() {
170 },
171 _ => (),
172 }
173 }
174 generics.clone()
175}