relm_derive_common/
lib.rs

1extern 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}