1#![recursion_limit="256"]
27
28extern crate proc_macro;
29
30mod gen;
31
32use quote::{quote, quote_spanned};
33use proc_macro2::TokenStream;
34use syn::{
35 GenericParam,
36 Generics,
37 Ident,
38 Item,
39 LifetimeDef,
40 TypeParam,
41 parse,
42};
43use syn::spanned::Spanned;
44
45use gen::{gen_widget, gen_where_clause, parser::dummy_ident};
46
47#[proc_macro_derive(Msg)]
48pub fn msg(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
49 let ast: Item = parse(input).expect("msg > parse failed");
50 let gen = impl_msg(&ast, Ident::new("relm", ast.span()));
51 gen.into()
52}
53
54#[proc_macro_attribute]
55pub fn widget(_attributes: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
56 let ast: Item = parse(input).expect("widget.parse failed");
57 let tokens = quote! {
58 #ast
59 };
60 let expanded = gen_widget(tokens);
61 expanded.into()
62}
63
64fn impl_msg(ast: &Item, krate: Ident) -> TokenStream {
65 let display = derive_display_variant(ast, &krate);
66 let into_option = derive_into_option(ast, &krate);
67
68 quote! {
69 #display
70 #into_option
71 }
72}
73
74fn derive_display_variant(ast: &Item, krate: &Ident) -> TokenStream {
75 if let Item::Enum(ref enum_item) = *ast {
76 let generics = &enum_item.generics;
77 let name = &enum_item.ident;
78 let generics_without_bound = remove_generic_bounds(generics);
79 let typ = quote! {
80 #name #generics_without_bound
81 };
82
83 let variant_patterns = enum_item.variants.iter().map(|variant| {
84 let doc_ident = dummy_ident("doc");
85 let attrs = variant.attrs.iter().filter(|attr| !attr.path.is_ident(&doc_ident));
86 let ident = &variant.ident;
87 quote! {
88 #(#attrs)* #name::#ident { .. }
89 }
90 });
91 let variant_names = enum_item.variants.iter().map(|variant| {
92 variant.ident.to_string()
93 });
94 let where_clause = gen_where_clause(generics);
95
96 quote_spanned! { krate.span() =>
97 impl #generics ::#krate::DisplayVariant for #typ #where_clause {
98 #[allow(unused_qualifications)]
99 fn display_variant(&self) -> &'static str {
100 match *self {
101 #(#variant_patterns => #variant_names,)*
102 }
103 }
104 }
105 }
106 }
107 else {
108 panic!("Expected enum");
109 }
110}
111
112fn derive_into_option(ast: &Item, krate: &Ident) -> TokenStream {
113 if let Item::Enum(ref enum_item) = *ast {
114 let generics = &enum_item.generics;
115 let name = &enum_item.ident;
116 let generics_without_bound = remove_generic_bounds(generics);
117 let typ = quote! {
118 #name #generics_without_bound
119 };
120 let where_clause = gen_where_clause(generics);
121
122 quote_spanned! { krate.span() =>
123 impl #generics ::#krate::IntoOption<#typ> for #typ #where_clause {
124 fn into_option(self) -> Option<#typ> {
125 Some(self)
126 }
127 }
128 }
129 }
130 else {
131 panic!("Expecting enum");
132 }
133}
134
135fn remove_generic_bounds(generics: &Generics) -> Generics {
136 let mut generics = generics.clone();
137 for param in generics.params.iter_mut() {
138 match *param {
139 GenericParam::Lifetime(LifetimeDef { ref mut bounds, .. }) =>
140 while bounds.pop().is_some() {
141 },
142 GenericParam::Type(TypeParam { ref mut bounds, .. }) =>
143 while bounds.pop().is_some() {
144 },
145 _ => (),
146 }
147 }
148 generics
149}