surjective_enum/
lib.rs

1//! Surjective mapping for conversion of enum representation to enmu.
2//! See the macro documentation and tests for example usage.
3
4extern 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/// Derive a surjective ::core::convert::From<Unitary Enum Representation> conversion function
16/// which maps all values which are not part of the enumeration to the last
17/// enum discriminant.
18///
19/// The example
20/// ``` rust
21/// use surjective_enum::From;
22/// #[repr(u8)]
23/// #[derive(From)]
24/// pub enum Enum {
25///     Bar  = 0b00,
26///     Foo  = 0b01,
27///     Rest = 0b11
28/// }
29/// ```
30/// will create a from(u8) -> Enum conversion function which maps
31/// 0 -> Bar, 1 -> Foo and all other values to Rest.
32#[proc_macro_derive(From)]
33pub fn from(input: TokenStream) -> TokenStream {
34    let ast: DeriveInput = parse( input).unwrap();
35    // get the representation of the enum
36    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" { // C -> isize                  
41                    // an representation was given
42                    rep = a.clone();
43                }
44            }
45        }
46    }
47    // iteration over all fields
48    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