1#![feature(const_type_id)]
5#![recursion_limit = "128"]
6
7extern crate proc_macro;
8extern crate proc_macro2;
9#[macro_use]
10extern crate quote;
11#[macro_use]
12extern crate syn;
13#[cfg(test)]
14extern crate tempfile;
15#[cfg(test)]
16extern crate type_info;
17
18#[cfg(test)]
19mod test;
20
21struct MetaType<'a> {
22 tokens: quote::Tokens,
23 ident: syn::Ident,
24 data: MetaData<'a>,
25}
26
27struct MetaData<'a> {
28 tokens: quote::Tokens,
29 fields: Option<MetaFields<'a>>,
30 variants: Option<Vec<MetaVariant<'a>>>,
31}
32
33struct MetaFields<'a> {
34 tokens: quote::Tokens,
35 kind: MetaFieldsKind,
36 fields: Vec<MetaField<'a>>,
37}
38
39enum MetaFieldsKind {
40 Unit,
41 Unnamed(usize),
42 Named(usize),
43}
44
45struct MetaField<'a> {
46 tokens: quote::Tokens,
47 id: MetaFieldId<'a>,
48}
49
50struct MetaVariant<'a> {
51 tokens: quote::Tokens,
52 id: MetaVariantId,
53 fields: MetaFields<'a>,
54}
55
56enum MetaFieldId<'a> {
57 Unnamed(syn::Index),
58 Named(&'a syn::Ident),
59}
60
61struct MetaVariantId(syn::Ident);
62
63enum MetaBorrow {
64 Ref,
65 Mut,
66}
67
68#[proc_macro_derive(TypeInfo, attributes(type_info))]
70pub fn type_info(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
71 let ast = syn::parse(input).unwrap();
72 let gen = impl_type_info(ast);
73 gen.into()
74}
75
76#[cfg(test)]
77pub fn type_info_test(input: proc_macro2::TokenStream) -> quote::Tokens {
78 let ast = syn::parse2(input).unwrap();
79 impl_type_info(ast)
80}
81
82fn impl_type_info(mut ast: syn::DeriveInput) -> quote::Tokens {
83 let ident = &ast.ident;
84
85 add_static(&mut ast.generics);
86
87 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
88
89 let type_info = build_type_info(&ast);
90 let tokens = &type_info.tokens;
91
92 let field_fn = build_field_fn(&type_info);
93 let field_mut_fn = build_field_mut_fn(&type_info);
94 let variant_fn = build_variant_fn(&type_info);
95 let field_any_fn = build_field_any_fn(&type_info);
96 let field_any_mut_fn = build_field_any_mut_fn(&type_info);
97
98 quote! {
99 impl #impl_generics ::type_info::TypeInfo for #ident #ty_generics #where_clause {
100 const TYPE: ::type_info::Type = #tokens;
101 #field_fn
102 #field_mut_fn
103 }
104
105 impl #impl_generics ::type_info::DynamicTypeInfo for #ident #ty_generics #where_clause {
106 fn type_ref(&self) -> &'static ::type_info::Type {
107 &<Self as ::type_info::TypeInfo>::TYPE
108 }
109
110 #variant_fn
111 #field_any_fn
112 #field_any_mut_fn
113 }
114 }
115}
116
117fn build_field_fn(type_info: &MetaType) -> quote::Tokens {
118 build_field_fn_body(
119 type_info,
120 |a| quote!({::std::any::Any::downcast_ref::<TypeInfoA>(#a)}),
121 MetaBorrow::Ref,
122 ).map(|body| {
123 quote! {
124 fn field<TypeInfoA>(&self, id: ::type_info::FieldId) -> ::std::option::Option<&TypeInfoA>
125 where
126 TypeInfoA: ::std::any::Any,
127 {
128 #body
129 }
130 }
131 })
132 .unwrap_or(quote!())
133}
134
135fn build_field_mut_fn(type_info: &MetaType) -> quote::Tokens {
136 build_field_fn_body(
137 type_info,
138 |a| quote!({::std::any::Any::downcast_mut::<TypeInfoA>(#a)}),
139 MetaBorrow::Mut,
140 ).map(|body| {
141 quote! {
142 fn field_mut<TypeInfoA>(&mut self, id: ::type_info::FieldId) -> ::std::option::Option<&mut TypeInfoA>
143 where
144 TypeInfoA: ::std::any::Any,
145 {
146 #body
147 }
148 }
149 })
150 .unwrap_or(quote!())
151}
152
153fn build_field_any_fn(type_info: &MetaType) -> quote::Tokens {
154 build_field_fn_body(
155 type_info,
156 |a| quote!(::std::option::Option::Some(#a),),
157 MetaBorrow::Ref,
158 ).map(|body| {
159 quote! {
160 fn field_any(&self, id: ::type_info::FieldId) -> ::std::option::Option<&::std::any::Any> {
161 #body
162 }
163 }
164 })
165 .unwrap_or(quote!())
166}
167
168fn build_field_any_mut_fn(type_info: &MetaType) -> quote::Tokens {
169 build_field_fn_body(
170 type_info,
171 |a| quote!(::std::option::Option::Some(#a),),
172 MetaBorrow::Mut,
173 ).map(|body| {
174 quote! {
175 fn field_any_mut(&mut self, id: ::type_info::FieldId) -> ::std::option::Option<&mut ::std::any::Any> {
176 #body
177 }
178 }
179 })
180 .unwrap_or(quote!())
181}
182
183fn build_field_fn_body<A>(
184 type_info: &MetaType,
185 accessor_builder: A,
186 meta_borrow: MetaBorrow,
187) -> Option<quote::Tokens>
188where
189 A: FnMut(quote::Tokens) -> quote::Tokens,
190{
191 if let Some(ref variants) = type_info.data.variants {
192 if variants.iter().all(|v| v.fields.fields.is_empty()) {
193 None
194 } else {
195 Some(build_field_fn_body_from_variants(
196 &type_info.ident,
197 variants,
198 accessor_builder,
199 meta_borrow,
200 ))
201 }
202 } else if let Some(MetaFields { ref fields, .. }) = type_info.data.fields {
203 if fields.is_empty() {
204 None
205 } else {
206 Some(build_field_fn_body_from_fields(
207 fields,
208 accessor_builder,
209 meta_borrow,
210 ))
211 }
212 } else {
213 None
214 }
215}
216
217fn build_field_fn_body_from_fields<A>(
218 meta_fields: &[MetaField],
219 mut accessor_builder: A,
220 meta_borrow: MetaBorrow,
221) -> quote::Tokens
222where
223 A: FnMut(quote::Tokens) -> quote::Tokens,
224{
225 let fields = meta_fields.iter().map(|f| match f.id {
226 MetaFieldId::Unnamed(ref i) => {
227 let i_usize = i.index as usize;
228 let accessor = accessor_builder(match meta_borrow {
229 MetaBorrow::Ref => quote!(&self.#i),
230 MetaBorrow::Mut => quote!(&mut self.#i),
231 });
232
233 quote! {
234 ::type_info::FieldId::Unnamed(#i_usize) => #accessor
235 }
236 }
237 MetaFieldId::Named(i) => {
238 let i_str = i.as_ref();
239 let accessor = accessor_builder(match meta_borrow {
240 MetaBorrow::Ref => quote!(&self.#i),
241 MetaBorrow::Mut => quote!(&mut self.#i),
242 });
243
244 quote! {
245 ::type_info::FieldId::Named(#i_str) => #accessor
246 }
247 }
248 });
249
250 quote! {
251 match id {
252 #(#fields)*
253 _ => ::std::option::Option::None,
254 }
255 }
256}
257
258fn build_field_fn_body_from_variants<A>(
259 type_ident: &syn::Ident,
260 meta_variants: &[MetaVariant],
261 mut accessor_builder: A,
262 meta_borrow: MetaBorrow,
263) -> quote::Tokens
264where
265 A: FnMut(quote::Tokens) -> quote::Tokens,
266{
267 let variants = meta_variants.iter().map(|v| {
268 let ident = v.id.0;
269 let meta_fields = &v.fields.fields;
270
271 let syn_idents = (0..meta_fields.len())
272 .map(|idx| syn::Ident::from(format!("_{}", idx).as_str()))
273 .collect::<Vec<_>>();
274 let pat_syn_idents = syn_idents.iter().map(|ident| match meta_borrow {
275 MetaBorrow::Ref => quote!(ref #ident),
276 MetaBorrow::Mut => quote!(ref mut #ident),
277 });
278
279 match v.fields.kind {
280 MetaFieldsKind::Unit => quote! { #type_ident::#ident => ::std::option::Option::None, },
281 MetaFieldsKind::Unnamed(_) => {
282 let body = build_field_fn_variant_field_match(
283 meta_fields,
284 &syn_idents,
285 &mut accessor_builder,
286 );
287
288 quote! { #type_ident::#ident(#(#pat_syn_idents,)*) => #body }
289 }
290 MetaFieldsKind::Named(_) => {
291 let pat_idents = meta_fields.iter().map(|f| match f.id {
292 MetaFieldId::Named(ident) => ident,
293 _ => unreachable!(),
294 });
295 let body = build_field_fn_variant_field_match(
296 meta_fields,
297 &syn_idents,
298 &mut accessor_builder,
299 );
300
301 quote! { #type_ident::#ident { #(#pat_idents: #pat_syn_idents,)* } => #body }
302 }
303 }
304 });
305
306 quote! {
307 match *self {
308 #(#variants)*
309 }
310 }
311}
312
313fn build_field_fn_variant_field_match<A>(
314 meta_fields: &[MetaField],
315 syn_idents: &[syn::Ident],
316 mut accessor_builder: A,
317) -> quote::Tokens
318where
319 A: FnMut(quote::Tokens) -> quote::Tokens,
320{
321 if meta_fields.is_empty() {
322 quote!(::std::option::Option::None,)
323 } else {
324 let fields = meta_fields
325 .iter()
326 .zip(syn_idents)
327 .map(|(f, syn_ident)| match f.id {
328 MetaFieldId::Unnamed(ref i) => {
329 let i_usize = i.index as usize;
330 let accessor = accessor_builder(quote!(#syn_ident));
331
332 quote! {
333 ::type_info::FieldId::Unnamed(#i_usize) => #accessor
334 }
335 }
336 MetaFieldId::Named(i) => {
337 let i_str = i.as_ref();
338 let accessor = accessor_builder(quote!(#syn_ident));
339
340 quote! {
341 ::type_info::FieldId::Named(#i_str) => #accessor
342 }
343 }
344 });
345
346 quote! {
347 match id {
348 #(#fields)*
349 _ => ::std::option::Option::None,
350 },
351 }
352 }
353}
354
355fn build_variant_fn(type_info: &MetaType) -> quote::Tokens {
356 match type_info.data.variants {
357 Some(ref meta_variants) if !meta_variants.is_empty() => {
358 let variants = meta_variants.iter().map(|v| {
359 let type_ident = type_info.ident;
360 let ident = v.id.0;
361 let ident_str = ident.as_ref();
362 match v.fields.kind {
363 MetaFieldsKind::Unit => {
364 quote! { #type_ident::#ident => ::std::option::Option::Some(#ident_str), }
365 }
366 MetaFieldsKind::Unnamed(_) => {
367 quote! { #type_ident::#ident( .. ) => ::std::option::Option::Some(#ident_str), }
368 }
369 MetaFieldsKind::Named(_) => {
370 quote! { #type_ident::#ident { .. } => ::std::option::Option::Some(#ident_str), }
371 }
372 }
373 });
374
375 quote! {
376 fn variant(&self) -> ::std::option::Option<&str> {
377 match *self {
378 #(#variants)*
379 }
380 }
381 }
382 }
383 _ => quote!(),
384 }
385}
386
387fn add_static(generics: &mut syn::Generics) {
388 for type_param in generics.type_params_mut() {
389 if !type_param.bounds.empty_or_trailing() {
390 type_param
391 .bounds
392 .push_punct(syn::token::Add::new(proc_macro2::Span::call_site()));
393 }
394 type_param
395 .bounds
396 .push_value(syn::TypeParamBound::Trait(parse_quote!(::std::any::Any)));
397 }
398}
399
400fn build_type_info(ast: &syn::DeriveInput) -> MetaType {
401 let ident = &ast.ident;
402 let (_, generics, _) = &ast.generics.split_for_impl();
403 let ident_str = ident.as_ref();
404 let data = build_data(&ast.data);
405 let tokens = {
406 let data = &data.tokens;
407 quote! {
408 ::type_info::Type {
409 id: ::type_info::TypeId::of::<#ident #generics>(),
410 module: module_path!(),
411 ident: #ident_str,
412 data: #data,
413 }
414 }
415 };
416 let ident = ast.ident.clone();
417
418 MetaType {
419 tokens,
420 ident,
421 data,
422 }
423}
424
425fn build_data(data: &syn::Data) -> MetaData {
426 match *data {
427 syn::Data::Struct(ref data_struct) => build_data_struct(data_struct).map_tokens(|tokens| {
428 quote! {
429 ::type_info::Data::Struct(#tokens)
430 }
431 }),
432 syn::Data::Enum(ref data_enum) => build_data_enum(data_enum).map_tokens(|tokens| {
433 quote! {
434 ::type_info::Data::Enum(#tokens)
435 }
436 }),
437 syn::Data::Union(ref data_union) => build_data_union(data_union).map_tokens(|tokens| {
438 quote! {
439 ::type_info::Data::Union(#tokens)
440 }
441 }),
442 }
443}
444
445fn build_data_struct(data_struct: &syn::DataStruct) -> MetaData {
446 let data_struct_fields = build_fields(&data_struct.fields);
447 let tokens = {
448 let fields = &data_struct_fields.tokens;
449 quote! {
450 ::type_info::DataStruct {
451 fields: #fields,
452 }
453 }
454 };
455
456 MetaData {
457 tokens,
458 fields: Some(data_struct_fields),
459 variants: None,
460 }
461}
462
463fn build_data_enum(data_enum: &syn::DataEnum) -> MetaData {
464 let variants = data_enum
465 .variants
466 .iter()
467 .map(build_variant)
468 .collect::<Vec<_>>();
469
470 let tokens = {
471 let variant_tokens = variants.iter().map(|v| &v.tokens);
472 quote! {
473 ::type_info::DataEnum {
474 variants: &[
475 #(#variant_tokens,)*
476 ],
477 }
478 }
479 };
480
481 MetaData {
482 tokens,
483 fields: None,
484 variants: Some(variants),
485 }
486}
487
488fn build_data_union(data_union: &syn::DataUnion) -> MetaData {
489 let fields_named = build_fields_named(&data_union.fields);
490 let tokens = {
491 let fields = &fields_named.tokens;
492 quote! {
493 ::type_info::DataUnion {
494 fields: #fields,
495 }
496 }
497 };
498
499 MetaData {
500 tokens,
501 fields: Some(fields_named),
502 variants: None,
503 }
504}
505
506fn build_variant(variant: &syn::Variant) -> MetaVariant {
507 let ident = variant.ident.as_ref();
508 let fields = build_fields(&variant.fields);
509 let tokens = {
510 let field_tokens = &fields.tokens;
511 quote! {
512 ::type_info::Variant {
513 ident: #ident,
514 fields: #field_tokens,
515 }
516 }
517 };
518
519 MetaVariant {
520 tokens,
521 id: MetaVariantId(variant.ident),
522 fields,
523 }
524}
525
526fn build_fields(fields: &syn::Fields) -> MetaFields {
527 match *fields {
528 syn::Fields::Named(ref fields_named) => {
529 build_fields_named(fields_named).map_tokens(|tokens| {
530 quote! {
531 ::type_info::Fields::Named(#tokens)
532 }
533 })
534 }
535 syn::Fields::Unnamed(ref fields_unnamed) => build_fields_unnamed(fields_unnamed)
536 .map_tokens(|tokens| {
537 quote! {
538 ::type_info::Fields::Unnamed(#tokens)
539 }
540 }),
541 syn::Fields::Unit => MetaFields {
542 tokens: quote! {
543 ::type_info::Fields::Unit
544 },
545 kind: MetaFieldsKind::Unit,
546 fields: vec![],
547 },
548 }
549}
550
551fn build_fields_named(fields_named: &syn::FieldsNamed) -> MetaFields {
552 let fields = fields_named
553 .named
554 .iter()
555 .enumerate()
556 .map(|(i, f)| build_field(i, f))
557 .collect::<Vec<_>>();
558 let tokens = {
559 let named = fields.iter().map(|f| &f.tokens);
560 quote! {
561 ::type_info::FieldsNamed {
562 named: &[
563 #(#named,)*
564 ],
565 }
566 }
567 };
568 let kind = MetaFieldsKind::Named(fields.len());
569
570 MetaFields {
571 tokens,
572 fields,
573 kind,
574 }
575}
576
577fn build_fields_unnamed(fields_unnamed: &syn::FieldsUnnamed) -> MetaFields {
578 let fields = fields_unnamed
579 .unnamed
580 .iter()
581 .enumerate()
582 .map(|(i, f)| build_field(i, f))
583 .collect::<Vec<_>>();
584 let tokens = {
585 let unnamed = fields.iter().map(|f| &f.tokens);
586 quote! {
587 ::type_info::FieldsUnnamed {
588 unnamed: &[
589 #(#unnamed,)*
590 ],
591 }
592 }
593 };
594 let kind = MetaFieldsKind::Unnamed(fields.len());
595
596 MetaFields {
597 tokens,
598 fields,
599 kind,
600 }
601}
602
603fn build_field(idx: usize, field: &syn::Field) -> MetaField {
604 match field.ident {
605 Some(ref ident) => {
606 let ident_str = ident.as_ref();
607 let ty = &field.ty;
608 let tokens = quote! {
609 ::type_info::Field {
610 id: ::type_info::FieldId::Named(#ident_str),
611 ident: ::std::option::Option::Some(#ident_str),
612 ty: <#ty as ::type_info::TryTypeInfo>::TRY_TYPE,
613 }
614 };
615 let id = MetaFieldId::Named(ident);
616
617 MetaField { tokens, id }
618 }
619 None => {
620 let ty = &field.ty;
621 let tokens = quote! {
622 ::type_info::Field {
623 id: ::type_info::FieldId::Unnamed( #idx),
624 ident: ::std::option::Option::None,
625 ty: <#ty as::type_info::TryTypeInfo >::TRY_TYPE,
626 }
627 };
628 let id = MetaFieldId::Unnamed(syn::Index {
629 index: idx as u32,
630 span: proc_macro2::Span::call_site(),
631 });
632
633 MetaField { tokens, id }
634 }
635 }
636}
637
638impl<'a> MetaData<'a> {
639 fn map_tokens<F>(self, mapper: F) -> Self
640 where
641 F: FnOnce(quote::Tokens) -> quote::Tokens,
642 {
643 MetaData {
644 tokens: mapper(self.tokens),
645 fields: self.fields,
646 variants: self.variants,
647 }
648 }
649}
650
651impl<'a> MetaFields<'a> {
652 fn map_tokens<F>(self, mapper: F) -> Self
653 where
654 F: FnOnce(quote::Tokens) -> quote::Tokens,
655 {
656 MetaFields {
657 tokens: mapper(self.tokens),
658 kind: self.kind,
659 fields: self.fields,
660 }
661 }
662}