from_num/
lib.rs

1//! Attribute macro,implementing the From trait for your Enum.
2//! 
3//! ```rust
4//! use from_num::from_num;
5//! #[derive(Debug,PartialEq)]
6//! #[from_num(i8,u64,usize)]
7//! enum Planet {
8//!     Mercury,
9//!     Venus,
10//!     Earth,
11//!     Mars,
12//!     Jupiter = 0b1000,
13//!     Saturn,
14//!     Uranus = 0xff,
15//!     Neptune
16//! }
17//! pub fn get_from_number() {
18//!     assert_eq!(Planet::Jupiter,Planet::from_i8(0b1000 as i8).unwrap());
19//!     assert_eq!(Planet::Neptune,Planet::from_u64(256 as u64).unwrap());
20//! }
21//! ```
22
23use proc_macro::{TokenStream, TokenTree};
24use proc_macro2::Ident;
25use quote::ToTokens;
26use syn::{parse_macro_input, Data, DeriveInput};
27extern crate proc_macro;
28
29/// Implements the From trait for your Enum.
30///
31/// # Examples
32/// 
33/// ```rust
34/// use from_num::from_num;
35/// #[derive(Debug,PartialEq)]
36/// #[from_num(i8,u64,usize)]
37/// enum Planet {
38///     Mercury,
39///     Venus,
40///     Earth,
41///     Mars,
42///     Jupiter = 0b1000,
43///     Saturn,
44///     Uranus = 0xff,
45///     Neptune
46/// }
47/// pub fn get_from_number() {
48///     assert_eq!(Planet::Jupiter,Planet::from_i8(0b1000 as i8).unwrap());
49///     assert_eq!(Planet::Neptune,Planet::from_u64(256 as u64).unwrap());
50/// }
51/// ```
52#[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}