1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
extern crate proc_macro; use proc_macro::TokenStream; use quote::{format_ident, quote}; #[proc_macro_attribute] pub fn type_code(args: TokenStream, input: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as syn::AttributeArgs); let input = syn::parse_macro_input!(input as syn::ItemEnum); if args.len() != 1 { panic!("The `type_code` attribute expects a single integer literal argument"); } let bits = &args[0]; let name = &input.ident; let mut variants = Vec::new(); let mut decodes = Vec::new(); let mut encodes = Vec::new(); let mut enumerator = 0; for variant in input.variants.iter() { let name = &variant.ident; let table = format_ident!("{}", name); if let Some((_, syn::Expr::Lit(value))) = &variant.discriminant { if let syn::Lit::Int(value) = &value.lit { enumerator = value.base10_parse::<u32>().unwrap(); } } variants.push(quote!( #name(winmd::#name), )); decodes.push(quote!( #enumerator => Self::#name(winmd:: #name(winmd::Row::new(code.1, winmd::TableIndex::#table, file))), )); encodes.push(quote!( Self::#name(value) => ((value.0.index + 1) << #bits) | #enumerator, )); enumerator += 1; } let output = quote!( #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)] pub enum #name { #(#variants)* } impl Decode for #name { fn decode(code: u32, file:u16) -> Self { let code = (code & ((1 << #bits) - 1), (code >> #bits) - 1); match code.0 { #(#decodes)* _ => panic!("Failed to decode type code"), } } } impl #name { pub fn encode(&self) -> u32 { match self { #(#encodes)* } } } ); output.into() }