1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#![crate_type = "proc-macro"]
extern crate syn;
#[macro_use]
extern crate quote;
extern crate proc_macro;
use proc_macro::TokenStream;
use std::iter::Iterator;
#[proc_macro_derive(EnumDisplay)]
pub fn emit_enum_display(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let toks = quote! {
impl ::std::fmt::Display for #name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{:?}", self)
}
}
};
toks.into()
}
#[proc_macro_derive(EnumError)]
pub fn emit_enum_error(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let en = match ast.data {
syn::Data::Enum(ref v) => v,
_ => panic!("EnumError only works for enum types"),
};
let mut serializations = Vec::new();
for variant in &en.variants {
let ident = &variant.ident;
let ident_str = format!("{}", ident);
serializations.push(quote! {
&#name::#ident => #ident_str
});
}
let toks = quote! {
impl ::std::error::Error for #name {
fn description(&self) -> &str {
match self {
#(#serializations),*
}
}
}
};
toks.into()
}
#[proc_macro_derive(EnumConvertFromInt, attributes(EnumConvertFromIntFallback))]
pub fn emit_enum_from_primitive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let en = match ast.data {
syn::Data::Enum(ref v) => v,
_ => panic!("EnumConvertFromInt only works for enum types"),
};
let mut serializations = Vec::new();
for variant in &en.variants {
let ident = &variant.ident;
let val = &match variant.discriminant {
Some(ref expr) => &expr.1,
None => panic!("{}::{} does not have an associated value", name, ident),
};
serializations.push(quote! {
#val => #name::#ident
});
}
let is_fallback_value = |attr: &syn::Attribute| match attr.parse_meta() {
Ok(syn::Meta::NameValue(syn::MetaNameValue { path, .. })) => {
path.is_ident("EnumConvertFromIntFallback")
}
_ => false,
};
let fallback = match ast.attrs.into_iter().find(&is_fallback_value) {
Some(attr) => match attr.parse_meta() {
Ok(syn::Meta::NameValue(syn::MetaNameValue {
lit: syn::Lit::Str(val),
..
})) => {
let id = syn::Ident::new(&val.value(), val.span());
quote! { #name::#id }
}
_ => panic!("EnumConvertFromIntFallback must be a string"),
},
None => quote! { panic!("Received unexpected value {}", val) },
};
let toks = quote! {
impl ::std::convert::From<i32> for #name {
fn from(val: i32) -> #name {
match val {
#(#serializations),*,
_ => #fallback
}
}
}
};
toks.into()
}