1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, DeriveInput};
6
7#[proc_macro_derive(FromNum)]
8pub fn add_from_num_implementation(input: TokenStream) -> TokenStream {
9 let input = parse_macro_input!(input as DeriveInput);
10
11 let name = &input.ident;
12 let variants = if let syn::Data::Enum(data_enum) = &input.data {
13 &data_enum.variants
14 } else {
15 panic!("EnumToNum can only be derived for enums.");
16 };
17
18 let has_values = variants.iter().any(|v| !v.fields.is_empty());
19
20 let to_type = match variants.len() {
21 0..=255 => quote! {u8},
22 256..=65535 => quote! {u16},
23 65536..=4294967295 => quote! {u32},
24 _ => panic!(
25 "Whoah there cowboy, how did you manage to get {} values in {}?",
26 variants.len(),
27 name
28 ),
29 };
30
31 let arms = variants.iter().enumerate().map(|(index, variant)| {
32 let variant_name = &variant.ident;
33 let to_value = index as u8;
34 if variant.fields.is_empty() {
35 quote! {
36 #name::#variant_name => #to_value,
37 }
38 } else {
39 match variant.fields {
40 syn::Fields::Named(_) => quote! {
41 #name::#variant_name{..} => #to_value,
42 },
43 syn::Fields::Unnamed(_) => quote! {
44 #name::#variant_name(..) => #to_value,
45 },
46 _ => quote! {
47 #name::#variant_name => #to_value,
48 },
49 }
50 }
51 });
52
53 let arms_cloned = arms.clone();
54
55 let mut expanded = quote! {
56 impl From<&#name> for #to_type {
57 fn from(value: &#name) -> Self {
58 match value {
59 #(#arms)*
60 }
61 }
62 }
63 };
64
65 if !has_values {
66 expanded = quote! {
67 #expanded
68
69 impl From<#name> for #to_type {
70 fn from(value: #name) -> Self {
71 match value {
72 #(#arms_cloned)*
73 }
74 }
75 }
76 }
77 }
78
79 TokenStream::from(expanded)
80}