1#![recursion_limit = "128"]
2extern crate proc_macro;
3
4use quote::quote;
5
6use proc_macro2::TokenStream;
7use quote::ToTokens;
8use syn::{Data, DeriveInput, Expr, ExprLit, Fields, Lit};
9
10#[proc_macro_derive(IntegerId)]
11pub fn integer_id(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
12 let ast = syn::parse(input).unwrap();
13 impl_integer_id(&ast).into()
14}
15
16#[allow(unused_variables)]
18fn impl_integer_id(ast: &DeriveInput) -> TokenStream {
19 let name = &ast.ident;
20 match ast.data {
21 Data::Struct(ref data) => {
22 let fields = &data.fields;
23 match fields.len() {
24 1 => {
25 let field = fields.iter().next().unwrap();
26 let field_type = &field.ty;
33 let (constructor, field_name) = match data.fields {
34 Fields::Named(_) => {
35 let field_name = field.ident.to_token_stream();
36 (quote!(#name { #field_name: value }), field_name)
37 }
38 Fields::Unnamed(_) => (quote! { #name( value ) }, quote!(0)),
39 Fields::Unit => unreachable!(),
40 };
41 quote! {
42 impl ::idmap::IntegerId for #name {
43 #[inline(always)]
44 fn from_id(id: u64) -> Self {
45 let value = <#field_type as ::idmap::IntegerId>::from_id(id);
46 #constructor
47 }
48 #[inline(always)]
49 fn id(&self) -> u64 {
50 <#field_type as ::idmap::IntegerId>::id(&self.#field_name)
51 }
52 #[inline(always)]
53 fn id32(&self) -> u32 {
54 <#field_type as ::idmap::IntegerId>::id32(&self.#field_name)
55 }
56 }
57 }
58 }
59 0 => panic!("`IntegerId` is currently unimplemented for empty structs"),
60 _ => panic!(
61 "`IntegerId` can only be applied to structs with a single field, but {} has {}",
62 name,
63 fields.len()
64 ),
65 }
66 }
67 Data::Enum(ref data) => {
68 let mut idx = 0;
69 let variants: Vec<_> = data.variants.iter().map(|variant| {
70 let ident = &variant.ident;
71 match variant.fields {
72 Fields::Unit => (),
73 _ => {
74 panic!("`IntegerId` can be applied only to unitary enums, {}::{} is either struct or tuple", name, ident)
75 },
76 }
77 match &variant.discriminant {
78 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(value), .. }))) => {
79 idx = value.base10_parse::<u64>().expect("Unable to parse discriminant");
80 },
81 Some(_) => panic!("Can't handle discriminant for {}::{}", name, ident),
82 None => {}
83 }
84 let tt = quote!(#idx => #name::#ident);
85 idx += 1;
86 tt
87 }).collect();
88 quote! {
89 impl ::idmap::IntegerId for #name {
90 #[inline]
91 fn from_id(id: u64) -> Self {
92 match id {
93 #(#variants,)*
94 _ => ::idmap::_invalid_id(id)
95 }
96 }
97 #[inline(always)]
98 fn id(&self) -> u64 {
99 *self as u64
100 }
101 #[inline(always)]
102 fn id32(&self) -> u32 {
103 *self as u32
104 }
105 }
106 }
107 }
108 Data::Union(_) => panic!("Unions are unsupported!"),
109 }
110}