1#![recursion_limit="512"]
2
3extern crate proc_macro;
4extern crate syn;
5#[macro_use]
6extern crate quote;
7extern crate proc_macro2;
8
9use proc_macro::TokenStream;
10use syn::DeriveInput;
11use quote::ToTokens;
12
13fn get_meta_items( attr: &syn::Attribute ) -> Option< Vec< syn::NestedMeta > > {
14 if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "reference" {
15 match attr.parse_meta() {
16 Ok( syn::Meta::List( meta ) ) => Some( meta.nested.into_iter().collect() ),
17 _ => {
18 panic!( "Unrecognized meta item type!" );
19 }
20 }
21 } else {
22 None
23 }
24}
25
26#[proc_macro_derive(ReferenceType, attributes(reference))]
51pub fn derive_reference_type( input: TokenStream ) -> TokenStream {
52 let input: proc_macro2::TokenStream = input.into();
53 let input: DeriveInput = syn::parse2( input ).unwrap();
54
55 let name = input.ident;
56 let generics_params = &input.generics.params;
57
58 let mut instance_of = None;
59 let mut event = None;
60 let mut subclass_of = Vec::new();
61
62 for meta_items in input.attrs.iter().filter_map( get_meta_items ) {
63 for meta in meta_items {
64 match meta {
65 syn::NestedMeta::Meta( syn::Meta::NameValue( ref meta ) ) if meta.path.to_token_stream().to_string() == "instance_of" => {
66 if instance_of.is_some() {
67 panic!( "Duplicate '#[reference(instance_of)]'!" );
68 }
69
70 if let syn::Lit::Str( ref str ) = meta.lit {
71 instance_of = Some( str.value() );
72 } else {
73 panic!( "The value of '#[reference(instance_of = ...)]' is not a string!" );
74 }
75 },
76 syn::NestedMeta::Meta( syn::Meta::NameValue( ref meta ) ) if meta.path.to_token_stream().to_string() == "event" => {
77 if event.is_some() {
78 panic!( "Duplicate '#[reference(event)]'!" );
79 }
80
81 if let syn::Lit::Str( ref str ) = meta.lit {
82 event = Some( str.value() );
83 } else {
84 panic!( "The value of '#[reference(event = ...)]' is not a string!" );
85 }
86 },
87 syn::NestedMeta::Meta( syn::Meta::List( ref meta ) ) if meta.path.to_token_stream().to_string() == "subclass_of" => {
88 for nested in &meta.nested {
89 match *nested {
90 syn::NestedMeta::Meta( ref nested ) => {
91 match *nested {
92 syn::Meta::Path( ref path ) => {
93 if let Some( ident ) = path.get_ident() {
94 subclass_of.push( ident.clone() );
95 } else {
96 panic!( "The value of '#[reference(subclass_of(...))]' is invalid!" )
97 }
98 },
99 _ => panic!( "The value of '#[reference(subclass_of(...))]' is invalid!" )
100 }
101 },
102 _ => panic!( "The value of '#[reference(subclass_of(...))]' is invalid!" )
103 }
104 }
105 },
106 syn::NestedMeta::Meta( ref meta ) => {
107 panic!( "Unrecognized attribute: '#[reference({})]'", meta.path().to_token_stream().to_string() );
108 },
109 _ => panic!( "Unrecognized attribute!" )
110 }
111 }
112 }
113
114 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
115
116 let mut default_args = Vec::new();
117 match input.data {
118 syn::Data::Struct( ref data ) => {
119 match data.fields {
120 syn::Fields::Unnamed( ref fields ) => {
121 fn invalid_structure() {
122 panic!( "The structure can only have either (Reference) or (Reference, PhantomData)!" );
123 }
124
125 let fields = &fields.unnamed;
126 match fields.len() {
127 1 => {},
128 2 => {
129 default_args.push( quote! {
130 std::default::Default::default()
131 });
132 },
133 _ => invalid_structure()
134 }
135
136 let mut fields_iter = fields.iter();
137 match fields_iter.next().unwrap().ty {
138 syn::Type::Path( ref ty_path ) => {
139 if ty_path.qself.is_some() {
140 invalid_structure();
141 }
142
143 let segs: Vec< _ > = ty_path.path.segments.iter().collect();
144 if segs.last().unwrap().ident != "Reference" || !segs.last().unwrap().arguments.is_empty() {
145 invalid_structure();
146 }
147
148 match segs.len() {
149 1 => {},
150 2 => {
151 if segs.first().unwrap().ident != "stdweb" {
152 invalid_structure();
153 }
154 },
155 _ => invalid_structure()
156 }
157 },
158 _ => invalid_structure()
159 }
160
161 match fields_iter.next().map( |field| &field.ty ) {
162 Some( &syn::Type::Path( ref ty_path ) ) => {
163 if ty_path.qself.is_some() {
164 invalid_structure();
165 }
166
167 let segs: Vec< _ > = ty_path.path.segments.iter().collect();
168 if segs.last().unwrap().ident != "PhantomData" {
169 invalid_structure();
170 }
171 },
172 Some( _ ) => invalid_structure(),
173 None => {}
174 }
175 },
176 _ => panic!( "Only tuple structures are supported!" )
177 }
178 },
179 _ => panic!( "Only tuple structures are supported!" )
180 }
181
182 let default_args = quote! { #(#default_args),* };
183
184 let mut instance_of_code = Vec::new();
185 if let Some( js_name ) = instance_of {
186 let code = format!( "o instanceof {}", js_name );
187 instance_of_code.push( code );
188 }
189
190 if let Some( ref event_name ) = event {
191 let code = format!( "o.type === \"{}\"", event_name );
192 instance_of_code.push( code );
193 }
194
195 let instance_of_impl = if !instance_of_code.is_empty() {
196 let mut code = String::new();
197 code.push_str( "var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );" );
198 code.push_str( "return (" );
199 code.push_str( &instance_of_code.join( " && " ) );
200 code.push_str( ");" );
201
202 quote! {
203 impl #impl_generics ::stdweb::InstanceOf for #name #ty_generics #where_clause {
204 #[inline]
205 fn instance_of( reference: &::stdweb::Reference ) -> bool {
206 __js_raw_asm_bool!(
207 #code,
208 reference.as_raw()
209 )
210 }
211 }
212 }
213 } else {
214 quote! {}
215 };
216
217 let concrete_event_impl = if let Some( event_name ) = event {
218 quote! {
219 impl #impl_generics ::stdweb::web::event::ConcreteEvent for #name #ty_generics {
220 const EVENT_TYPE: &'static str = #event_name;
221 }
222 }
223 } else {
224 quote! {}
225 };
226
227 let subclass_of_impl: Vec< _ > = subclass_of.into_iter().map( |target| {
228 let target: syn::Ident = target.into();
229 quote! {
230 impl #impl_generics From< #name #ty_generics > for #target #where_clause {
231 #[inline]
232 fn from( value: #name #ty_generics ) -> Self {
233 let reference: ::stdweb::Reference = value.into();
234 unsafe {
235 <#target as ::stdweb::ReferenceType>::from_reference_unchecked( reference )
236 }
237 }
238 }
239
240 impl #impl_generics ::stdweb::unstable::TryFrom< #target > for #name #ty_generics #where_clause {
241 type Error = ::stdweb::private::ConversionError;
242
243 #[inline]
244 fn try_from( value: #target ) -> Result< Self, Self::Error > {
245 use ::stdweb::unstable::TryInto;
246 let reference: ::stdweb::Reference = value.into();
247 reference.try_into()
248 }
249 }
250 }
251 }).collect();
252
253 let expanded = quote! {
254 #(#subclass_of_impl)*
255 #instance_of_impl
256 #concrete_event_impl
257
258 impl #impl_generics AsRef< ::stdweb::Reference > for #name #ty_generics #where_clause {
259 #[inline]
260 fn as_ref( &self ) -> &::stdweb::Reference {
261 &self.0
262 }
263 }
264
265 impl #impl_generics ::stdweb::ReferenceType for #name #ty_generics #where_clause {
266 #[inline]
267 unsafe fn from_reference_unchecked( reference: ::stdweb::Reference ) -> Self {
268 #name( reference, #default_args )
269 }
270 }
271
272 impl #impl_generics From< #name #ty_generics > for ::stdweb::Reference #where_clause {
273 #[inline]
274 fn from( value: #name #ty_generics ) -> Self {
275 value.0
276 }
277 }
278
279 impl #impl_generics ::stdweb::unstable::TryFrom< #name #ty_generics > for ::stdweb::Reference #where_clause {
280 type Error = ::stdweb::unstable::Void;
281
282 #[inline]
283 fn try_from( value: #name #ty_generics ) -> Result< Self, Self::Error > {
284 Ok( value.0 )
285 }
286 }
287
288 impl #impl_generics ::stdweb::unstable::TryFrom< ::stdweb::Reference > for #name #ty_generics #where_clause {
289 type Error = ::stdweb::private::ConversionError;
290
291 #[inline]
292 fn try_from( reference: ::stdweb::Reference ) -> Result< Self, Self::Error > {
293 reference.downcast()
294 .ok_or_else( || ::stdweb::private::ConversionError::Custom( "reference is of a different type".into() ) )
295 }
296 }
297
298 impl< '_r, #generics_params > ::stdweb::unstable::TryFrom< &'_r ::stdweb::Reference > for #name #ty_generics #where_clause {
299 type Error = ::stdweb::private::ConversionError;
300
301 #[inline]
302 fn try_from( reference: &::stdweb::Reference ) -> Result< Self, Self::Error > {
303 use ::stdweb::unstable::TryInto;
304 reference.clone().try_into()
305 }
306 }
307
308 impl #impl_generics ::stdweb::unstable::TryFrom< ::stdweb::Value > for #name #ty_generics #where_clause {
309 type Error = ::stdweb::private::ConversionError;
310
311 #[inline]
312 fn try_from( value: ::stdweb::Value ) -> Result< Self, Self::Error > {
313 use ::stdweb::unstable::TryInto;
314 let reference: ::stdweb::Reference = value.try_into()?;
315 reference.downcast()
316 .ok_or_else( || ::stdweb::private::ConversionError::Custom( "reference is of a different type".into() ) )
317 }
318 }
319
320 impl< '_r, #generics_params > ::stdweb::unstable::TryFrom< &'_r ::stdweb::Value > for #name #ty_generics #where_clause {
321 type Error = ::stdweb::private::ConversionError;
322
323 #[inline]
324 fn try_from( value: &::stdweb::Value ) -> Result< Self, Self::Error > {
325 use ::stdweb::unstable::TryInto;
326 let reference: &::stdweb::Reference =
327 value.as_reference()
328 .ok_or_else( || ::stdweb::private::ConversionError::Custom( "not a reference".into() ) )?;
329
330 reference.try_into()
331 }
332 }
333
334 impl #impl_generics ::stdweb::private::JsSerialize for #name #ty_generics #where_clause {
335 #[doc(hidden)]
336 #[inline]
337 fn _into_js< 'a >( &'a self ) -> ::stdweb::private::SerializedValue< 'a > {
338 self.0._into_js()
339 }
340 }
341
342 impl #impl_generics ::stdweb::private::JsSerializeOwned for #name #ty_generics #where_clause {
343 #[inline]
344 fn into_js_owned< '_a >( value: &'_a mut Option< Self > ) -> ::stdweb::private::SerializedValue< '_a > {
345 ::stdweb::private::JsSerialize::_into_js( value.as_ref().unwrap() )
346 }
347 }
348
349 impl< '_r, #generics_params > ::stdweb::private::JsSerializeOwned for &'_r #name #ty_generics #where_clause {
350 #[inline]
351 fn into_js_owned< '_a >( value: &'_a mut Option< Self > ) -> ::stdweb::private::SerializedValue< '_a > {
352 ::stdweb::private::JsSerialize::_into_js( value.unwrap() )
353 }
354 }
355 };
356
357 expanded.into()
358}