1
2extern crate proc_macro;
3extern crate syn;
4#[macro_use]
5extern crate quote;
6
7use proc_macro::TokenStream;
8use quote::ToTokens;
9use syn::{Data, DeriveInput, Field, Fields, Variant};
10
11#[proc_macro_derive(EnumError)]
12pub fn enum_error(input: TokenStream) -> TokenStream {
13 let ast = into_ast(input);
14 let froms = impl_into_enum(&ast);
15 let display = impl_display(&ast);
16 let error = impl_error(&ast);
17 let tokens = quote!{ #froms #display #error };
18 tokens.into()
19}
20
21#[proc_macro_derive(IntoEnum)]
22pub fn into_enum(input: TokenStream) -> TokenStream {
23 let ast = into_ast(input);
24 let froms = impl_into_enum(&ast);
25 let tokens = quote!{ #froms };
26 tokens.into()
27}
28
29#[proc_macro_derive(NewType)]
30pub fn newtype(input: TokenStream) -> TokenStream {
31 let ast = into_ast(input);
32 let from = impl_newtype_from(&ast);
33 let deref = impl_newtype_deref(&ast);
34 let tokens = quote!{ #from #deref };
35 tokens.into()
36}
37
38fn into_ast(input: TokenStream) -> DeriveInput {
39 syn::parse(input).unwrap()
40}
41
42fn get_enum_variants(ast: &DeriveInput) -> Vec<&Variant> {
43 match ast.data {
44 Data::Enum(ref inner) => inner.variants.iter().collect(),
45 Data::Struct(_) => unreachable!("Structs are not supported"),
46 Data::Union(_) => unreachable!("Unions are not supported"),
47 }
48}
49
50fn get_basetype(ast: &DeriveInput) -> &Field {
51 match ast.data {
52 Data::Enum(_) => unreachable!("Enums are not supported"),
53 Data::Union(_) => unreachable!("Unions are not supported"),
54 Data::Struct(ref ds) => {
55 match ds.fields {
56 Fields::Named(_) => unreachable!("Must be tuple struct"),
57 Fields::Unnamed(ref t) => {
58 if t.unnamed.len() > 1 {
59 unreachable!("Must be one type");
60 }
61 &t.unnamed[0]
62 }
63 Fields::Unit => unreachable!("Must be tuple struct"),
64 }
65 }
66 }
67}
68
69fn impl_into_enum(ast: &DeriveInput) -> Box<ToTokens> {
70 let name = &ast.ident;
71 let variants = get_enum_variants(&ast);
72 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
73 let impls = variants.iter().map(|var| {
74 let v = &var.ident;
75 let cont = match var.fields {
76 Fields::Unnamed(ref c) => &c.unnamed,
77 _ => unreachable!(),
78 };
79 assert!(cont.len() == 1, "Single tuple is required");
80 let ctype = &cont[0].ty;
81 quote!{
82 impl #impl_generics From<#ctype> for #name #ty_generics #where_clause {
83 fn from(val: #ctype) -> Self {
84 #name::#v(val)
85 }
86 }
87 }
88 });
89 Box::new(quote!{ #(#impls)* })
90}
91
92fn impl_newtype_from(ast: &DeriveInput) -> Box<ToTokens> {
93 let name = &ast.ident;
94 let field = get_basetype(&ast);
95 let base = &field.ty;
96 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
97
98 Box::new(
99 quote!{
100 impl #impl_generics From<#base> for #name #ty_generics #where_clause {
101 fn from(val: #base) -> Self {
102 #name(val)
103 }
104 }
105
106 impl #impl_generics Into<#base> for #name #ty_generics #where_clause {
107 fn into(self) -> #base {
108 self.0
109 }
110 }
111 }
112 )
113}
114
115fn impl_newtype_deref(ast: &DeriveInput) -> Box<ToTokens> {
116 let name = &ast.ident;
117 let field = get_basetype(&ast);
118 let base = &field.ty;
119 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
120 Box::new(
121 quote!{
122 impl #impl_generics ::std::ops::Deref for #name #ty_generics #where_clause {
123 type Target = #base;
124 fn deref(&self) -> &Self::Target { &self.0 }
125 }
126 impl #impl_generics ::std::ops::DerefMut for #name #ty_generics #where_clause {
127 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
128 }
129 }
130 )
131}
132
133fn impl_error(ast: &DeriveInput) -> Box<ToTokens> {
134 let name = &ast.ident;
135 let variants = get_enum_variants(&ast);
136 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
137 let snips = variants.iter().map(|var| {
138 let v = &var.ident;
139 quote!{ #name::#v(ref err) => err.description() }
140 });
141 Box::new(
142 quote!{
143 impl #impl_generics ::std::error::Error for #name #ty_generics #where_clause {
144 fn description(&self) -> &str {
145 match *self {
146 #(#snips), *
147 }
148 }
149 }
150 }
151 )
152}
153
154fn impl_display(ast: &DeriveInput) -> Box<ToTokens> {
155 let name = &ast.ident;
156 let variants = get_enum_variants(&ast);
157 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
158 let snips = variants.iter().map(|var| {
159 let v = &var.ident;
160 quote!{ #name::#v(ref err) => err.fmt(f) }
161 });
162 Box::new(
163 quote!{
164 impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause {
165 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
166 match *self {
167 #(#snips), *
168 }
169 }
170 }
171 }
172 )
173}