enum_tryfrom_derive/
lib.rs1#![cfg_attr(feature="clippy", feature(plugin))]
4#![cfg_attr(feature="clippy", plugin(clippy))]
5
6extern crate proc_macro;
7extern crate syn;
8#[macro_use]
9extern crate quote;
10
11use proc_macro::TokenStream;
12
13fn filter_primitive_type_attr(attr: &syn::Attribute) -> Option<&str> {
14 use syn::MetaItem::NameValue;
15
16 match attr.value {
17 NameValue(ref ident, syn::Lit::Str(ref value, _)) => {
18 if ident == "TryFromPrimitiveType" {
19 Some(value)
20 } else {
21 None
22 }
23 },
24 _ => None,
25 }
26}
27
28fn impl_single_type<'a, I: Iterator<Item=&'a syn::Variant>>
29 (name: &syn::Ident, ty: &syn::Ident, variants: I) -> quote::Tokens {
30 let blocks = variants.map(|var| {
31 let ident = &var.ident;
32 if var.data != syn::VariantData::Unit {
33 panic!("Enum variant may not store data!")
34 }
35 quote!{
36 if v == #name::#ident as #ty {
37 Ok(#name::#ident)
38 }
39 }
40 });
41 let mut tokens = quote::Tokens::new();
42 tokens.append_separated(blocks, "else");
43
44 quote! {
45 impl TryFrom<#ty> for #name {
46 type Error = enum_tryfrom::InvalidEnumValue;
47
48 fn try_from(v: #ty) -> Result<Self, Self::Error> {
49 #tokens else {
50 Err(Self::Error::new())
51 }
52 }
53 }
54 }
55}
56
57fn impl_from_primitive(ast: &syn::DeriveInput) -> quote::Tokens {
58 let name = &ast.ident;
59 let types = ast.attrs.iter().filter_map(filter_primitive_type_attr)
60 .map(syn::Ident::new);
61 let variants =
62 if let syn::Body::Enum(ref variants) = ast.body {
63 variants
64 } else {
65 panic!("`TryFromPrimitive` is only supported on Enums")
66 };
67 let impls = types.map(|ty| impl_single_type(name, &ty, variants.iter()));
68
69 let mut tokens = quote::Tokens::new();
70 tokens.append_all(impls);
71 tokens
72}
73
74#[proc_macro_derive(TryFromPrimitive, attributes(TryFromPrimitiveType))]
103pub fn from_primitive(input: TokenStream) -> TokenStream {
104 let s = input.to_string();
105 let ast = syn::parse_derive_input(&s).unwrap();
106 let gen = impl_from_primitive(&ast);
107
108 gen.parse().unwrap()
109}