1extern crate proc_macro;
5use proc_macro::TokenStream;
6use quote::*;
7use proc_macro2::Span;
8use syn::*;
9use syn::Meta::List;
10use syn::Meta::Word;
11use syn::punctuated::Pair::End;
12use syn::NestedMeta::Meta;
13use std::iter;
14
15#[proc_macro_derive(From)]
33pub fn from(input: TokenStream) -> TokenStream {
34 let ast: DeriveInput = parse( input).unwrap();
35 let mut rep: Ident = Ident::new("isize",Span::call_site());
37 if let Some( r) = ast.attrs.iter().find(|&a| a.path.is_ident("repr")) {
38 if let Ok( List( l) ) = r.parse_meta() {
39 if let Some( End( Meta( Word ( a) ) ) ) = l.nested.first() {
40 if format!("{}",a) != "C" { rep = a.clone();
43 }
44 }
45 }
46 }
47 let mut ret: TokenStream = quote!{}.into();
49
50 if let Data::Enum( e) = ast.data {
51 let mut prev_expr: Option<Expr> = None;
52 let (names, discrs): (Vec<_>, Vec<_>) = e.variants.iter()
53 .map(|x| {
54 match x.fields {
55 Fields::Named(_) | Fields::Unnamed(_) =>
56 panic!("the enum's fields must \
57 be in the \"ident = discriminant\" form"),
58 Fields::Unit => ()
59 }
60 let expr = match x.discriminant.as_ref() {
61 Some(discr) => discr.1.clone(),
62 None => match prev_expr {
63 Some(ref old_expr) => parse_quote!( 1 + #old_expr ),
64 None => parse_quote!( 0 ),
65 }
66 };
67 prev_expr = Some(expr.clone());
68 ( x.ident.clone(), expr )
69 }).unzip();
70 let ty = ast.ident.clone();
71 let vars_len = e.variants.len();
72 let ty_repeat = iter::repeat(ty.clone()).take(vars_len);
73 match names.last() {
74 Some( last_name) => {
75 let last_name = last_name.clone();
76 ret = quote! {
77 impl From<#rep> for #ty {
78 fn from(x: #rep) -> Self {
79 match x {
80 #( x if x == #discrs => #ty_repeat::#names, )*
81 _ => #ty::#last_name
82 }
83 }
84 }
85 impl From<#ty> for #rep {
86 fn from(x: #ty) -> Self { x as #rep }
87 }
88 }.into();
89 }
90 _ => {}
91 }
92 } else {
93 panic!("surjective_enum::From is only for enums");
94 }
95 ret
96}
97
98
99