1use proc_macro::{TokenStream, TokenTree};
24use proc_macro2::Ident;
25use quote::ToTokens;
26use syn::{parse_macro_input, Data, DeriveInput};
27extern crate proc_macro;
28
29#[proc_macro_attribute]
53pub fn from_num(attrs: TokenStream, item: TokenStream) -> TokenStream {
54 let attrs = attrs
55 .into_iter()
56 .filter(|v| match v {
57 TokenTree::Ident(_) => true,
58 _ => false,
59 })
60 .collect::<Vec<_>>();
61 let mut item_c = item.clone();
62 let input = parse_macro_input!(item as DeriveInput);
63 match input.data {
64 Data::Enum(data) => {
65 let mut arms = format!("");
66 let mut count = 0;
67 for var in data.variants.iter() {
68 if let Some((_, expr)) = var.discriminant.clone() {
69 count = parse_with_prefix(&expr.into_token_stream().to_string());
70 };
71 arms += &format! {
72 "{} => Ok(Self::{}),",
73 count,
74 var.ident.to_string()
75 };
76 count += 1;
77 }
78 let code = num_traits(&input.ident, &arms, &attrs);
79 item_c.extend(code.parse::<TokenStream>().unwrap());
80 item_c
81 }
82 _ => unimplemented!(),
83 }
84}
85
86fn num_traits(name: &Ident, arms: &str, nums: &[TokenTree]) -> String {
87 let mut code = String::new();
88 for token in nums.iter() {
89 code += &format! {
90 r#"impl {0} {{ pub fn from_{2}(value:{2}) -> anyhow::Result<Self> {{ match value {{ {1}_ => Err(anyhow::anyhow!("[enum {0}] Failed convertion from {{}}",value)) }} }} }}"#,
91 name,
92 arms,
93 token.to_string(),
94 };
95 }
96 code
97}
98
99fn parse_with_prefix(s: &str) -> usize {
100 let radix = match s {
101 s if s.starts_with("0b") => 2,
102 s if s.starts_with("0o") => 8,
103 s if s.starts_with("0x") => 16,
104 _ => {
105 return usize::from_str_radix(s, 10).unwrap();
106 }
107 };
108 usize::from_str_radix(&s[2..], radix).unwrap()
109}