typemap_meta_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{self, Attribute, Data, Fields};
6
7/// Add static type-to-value getters to a tuple struct containing disjoint heterogeneous types
8#[proc_macro_derive(Typemap, attributes(typemap_mut))]
9pub fn typemap_macro_derive(input: TokenStream) -> TokenStream {
10    // Construct a representation of Rust code as a syntax tree
11    // that we can manipulate
12    let ast = syn::parse(input).unwrap();
13
14    // Build the trait implementation
15    impl_typemap_macro(&ast)
16}
17
18fn impl_typemap_macro(ast: &syn::DeriveInput) -> TokenStream {
19    let struct_data = if let Data::Struct(s) = &ast.data {
20        s
21    } else {
22        panic!("Typemap only applies to tuple struct, but used on a non-struct!")
23    };
24    let tuple_fields = if let Fields::Unnamed(f) = &struct_data.fields {
25        f
26    } else {
27        panic!("Typemap only applies to tuple struct, but used on a non-tuple struct!")
28    };
29    let all_mut = has_mut_attr(&ast.attrs);
30
31    let types: Vec<_> = tuple_fields
32        .unnamed
33        .iter()
34        .map(|e| e.ty.to_token_stream())
35        .collect();
36    let indices: Vec<_> = (0..types.len()).map(syn::Index::from).collect();
37    let name = &ast.ident;
38    let generics = &ast.generics;
39    let gen = quote! {
40        #(impl #generics Get<#types> for #name #generics {
41            fn get(&self) -> &#types {
42                &self.#indices
43            }
44        })*
45    };
46    let gen_mut = if all_mut {
47        Some(quote! {
48            #(impl #generics GetMut<#types> for #name #generics {
49                fn get_mut(&mut self) -> &mut #types {
50                    &mut self.#indices
51                }
52            })*
53        })
54    } else {
55        None
56    };
57
58    quote! {
59        #gen
60        #gen_mut
61    }
62    .into()
63}
64
65fn has_mut_attr(attrs: &[Attribute]) -> bool {
66    attrs.iter().any(|attr| attr.path.is_ident("typemap_mut"))
67}