1#![recursion_limit = "128"]
2
3extern crate proc_macro;
4extern crate proc_macro2;
5extern crate syn;
6#[macro_use]
7extern crate quote;
8
9mod attr;
10mod codegen;
11mod format;
12mod plan;
13
14use proc_macro::TokenStream;
15
16#[proc_macro_derive(Protocol, attributes(protocol))]
17pub fn protocol(input: TokenStream) -> TokenStream {
18 let ast: syn::DeriveInput = syn::parse(input).unwrap();
20
21 let gen = impl_parcel(&ast);
23
24 gen.to_string().parse().expect("Could not parse generated parcel impl")
26}
27
28fn impl_parcel(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
30 match ast.data {
31 syn::Data::Struct(ref s) => impl_parcel_for_struct(ast, s),
32 syn::Data::Enum(ref e) => {
33 let plan = plan::Enum::new(ast, e);
34
35 let mut stream = impl_parcel_for_enum(&plan, ast);
36 stream.extend(impl_enum_for_enum(&plan, ast));
37 stream
38 },
39 syn::Data::Union(..) => unimplemented!(),
40 }
41}
42
43fn build_generics(ast: &syn::DeriveInput) -> (Vec<proc_macro2::TokenStream>, Vec<proc_macro2::TokenStream>) {
47 use quote::ToTokens;
48
49 let mut where_predicates = Vec::new();
50 let mut generics = Vec::new();
51
52 generics.extend(ast.generics.type_params().map(|t| {
53 let (ident, bounds) = (&t.ident, &t.bounds);
54 where_predicates.push(quote!(#ident : protocol::Parcel + #bounds));
55 quote!(#ident)
56 }));
57
58 generics.extend(ast.generics.lifetimes().enumerate().map(|(i, _)| {
59 let letter = ('a' as u8 + i as u8) as char;
60 quote!(#letter)
61 }));
62
63 if let Some(where_clause) = ast.generics.where_clause.clone() {
64 where_predicates.push(where_clause.predicates.into_token_stream());
65 }
66
67 assert!(ast.generics.const_params().next().is_none(),
68 "constant parameters are not supported yet");
69
70 (generics, where_predicates)
71}
72
73fn impl_parcel_for_struct(ast: &syn::DeriveInput,
74 strukt: &syn::DataStruct) -> proc_macro2::TokenStream {
75 let strukt_name = &ast.ident;
76 let read_fields = codegen::read_fields(&strukt.fields);
77 let write_fields = codegen::write_fields(&strukt.fields);
78
79 impl_trait_for(ast, quote!(protocol::Parcel), quote! {
80 const TYPE_NAME: &'static str = stringify!(#strukt_name);
81
82 #[allow(unused_variables)]
83 fn read_field(__io_reader: &mut io::Read,
84 __settings: &protocol::Settings,
85 _: &mut protocol::hint::Hints)
86 -> protocol::Result<Self> {
87 let mut __hints = protocol::hint::Hints::default();
89 __hints.begin_fields();
90
91 Ok(#strukt_name # read_fields)
92 }
93
94 #[allow(unused_variables)]
95 fn write_field(&self, __io_writer: &mut io::Write,
96 __settings: &protocol::Settings,
97 _: &mut protocol::hint::Hints)
98 -> protocol::Result<()> {
99 let mut __hints = protocol::hint::Hints::default();
101 __hints.begin_fields();
102
103 #write_fields
104 Ok(())
105 }
106 })
107}
108
109fn impl_parcel_for_enum(plan: &plan::Enum,
111 ast: &syn::DeriveInput)
112 -> proc_macro2::TokenStream {
113
114 let enum_name = &plan.ident;
115 let read_variant = codegen::enums::read_variant(plan);
116 let write_variant = codegen::enums::write_variant(plan);
117
118 impl_trait_for(ast, quote!(protocol::Parcel), quote! {
119 const TYPE_NAME: &'static str = stringify!(#enum_name);
120
121 #[allow(unused_variables)]
122 fn read_field(__io_reader: &mut io::Read,
123 __settings: &protocol::Settings,
124 _: &mut protocol::hint::Hints)
125 -> protocol::Result<Self> {
126 let mut __hints = protocol::hint::Hints::default();
128 __hints.begin_fields();
129
130 Ok(#read_variant)
131 }
132
133 #[allow(unused_variables)]
134 fn write_field(&self, __io_writer: &mut io::Write,
135 __settings: &protocol::Settings,
136 _: &mut protocol::hint::Hints)
137 -> protocol::Result<()> {
138 let mut __hints = protocol::hint::Hints::default();
140 __hints.begin_fields();
141
142 #write_variant
143
144 Ok(())
145 }
146 })
147}
148
149fn impl_enum_for_enum(plan: &plan::Enum,
150 ast: &syn::DeriveInput)
151 -> proc_macro2::TokenStream {
152 let enum_ident = &plan.ident;
153 let discriminant = plan.discriminant();
154
155 let variant_matchers = plan.variants.iter().map(|variant| {
156 let variant_ident = &variant.ident;
157 let discriminator = variant.discriminator_expr();
158 let fields_expr = variant.ignore_fields_pattern_expr();
159
160 quote!(#enum_ident::#variant_ident #fields_expr => {
161 #discriminator
162 })
163 });
164
165 impl_trait_for(ast, quote!(protocol::Enum), quote!(
166 type Discriminant = #discriminant;
167
168 fn discriminator(&self) -> Self::Discriminant {
169 match *self {
170 #(#variant_matchers)*
171 }
172 }
173 ))
174}
175
176fn anonymous_constant_block(description: &str,
180 item_name: &syn::Ident,
181 body: proc_macro2::TokenStream)
182 -> proc_macro2::TokenStream {
183 let anon_const_name = syn::Ident::new(&format!("__{}_FOR_{}",
184 description.replace(" ", "_").replace("::", "_"),
185 item_name.to_owned()),
186 proc_macro2::Span::call_site());
187
188 quote! {
189 #[allow(non_upper_case_globals)]
190 const #anon_const_name: () = {
191 extern crate protocol;
192 use std::io;
193
194 #body
195 };
196 }
197}
198
199fn impl_trait_for(ast: &syn::DeriveInput,
200 trait_name: proc_macro2::TokenStream,
201 impl_body: proc_macro2::TokenStream)
202 -> proc_macro2::TokenStream {
203 let item_name = &ast.ident;
204 let description = format!("impl {}", trait_name);
205
206 let (generics, where_predicates) = build_generics(ast);
207 let (generics, where_predicates) = (&generics, where_predicates);
208
209 anonymous_constant_block(&description, item_name, quote! {
210 impl < #(#generics),* > #trait_name for #item_name < #(#generics),* >
211 where #(#where_predicates),* {
212 #impl_body
213 }
214 })
215}
216