stupid_from_num/
lib.rs

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}