error_chain_mini_derive/
lib.rs1extern crate proc_macro;
4extern crate syn;
5#[macro_use]
6extern crate synstructure;
7#[macro_use]
8extern crate quote;
9
10decl_derive!([ErrorKind, attributes(msg)] => error_kind_derive);
11
12fn error_kind_derive(s: synstructure::Structure) -> quote::Tokens {
13 let short_body = s.each_variant(|v| {
14 if let Some(msg) = find_msg(&v.ast().attrs) {
15 let metas = msg.nested;
16 if metas.is_empty() {
17 panic!("You have to implement `#[msg(short = \"your description\")]`");
18 }
19 let mut filterd = metas.iter().filter_map(process_short);
20 if let Some(tokens) = filterd.next() {
21 if filterd.next().is_some() {
22 panic!("Cannot implment short=.. multiple times");
23 }
24 tokens
25 } else {
26 panic!("You have to implement `short = ..` attribute")
27 }
28 } else {
29 let par_name = s.ast().ident.as_ref();
30 let variant_name = v.ast().ident.as_ref();
31 if par_name != variant_name {
32 quote!(concat!(#par_name, "::", #variant_name))
33 } else {
34 quote!(#par_name)
35 }
36 }
37 });
38 let mut has_detailed = false;
39 let detailed_body = s.each_variant(|v| {
40 if let Some(t) = process_detailed(v) {
41 has_detailed = true;
42 t
43 } else {
44 quote!(String::new())
45 }
46 });
47 let mut res = s.bound_impl(
48 quote!(::error_chain_mini::ErrorKind),
49 quote! {
50 fn short(&self) -> &str {
51 match *self { #short_body }
52 }
53 fn detailed(&self) -> String {
54 match *self { #detailed_body }
55 }
56 },
57 );
58 let display_body = if has_detailed {
59 quote!(write!(f, "{} {{ {} }}", self.short(), self.detailed()))
60 } else {
61 quote!(write!(f, "{}", self.short()))
62 };
63 let display = s.bound_impl(
64 quote!(::std::fmt::Display),
65 quote! {
66 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
67 #display_body
68 }
69 },
70 );
71 res.append_all(display);
72 res
73}
74
75fn process_detailed(variant: &synstructure::VariantInfo) -> Option<quote::Tokens> {
76 let msg = find_msg(&variant.ast().attrs)?;
77 let nested = msg.nested;
78 let mut iter = nested.iter().skip_while(|nested| match nested {
79 syn::NestedMeta::Meta(syn::Meta::NameValue(nameval)) => nameval.ident != "detailed",
80 _ => panic!("Invlaid Value"),
81 });
82 let detailed = iter.next()?;
83 let s = if let syn::NestedMeta::Meta(syn::Meta::NameValue(nameval)) = detailed {
84 if let syn::Lit::Str(ref lit) = nameval.lit {
85 lit.value()
86 } else {
87 panic!("Only string is allowed after detailed=")
88 }
89 } else {
90 unreachable!();
91 };
92 macro_rules! get_nth {
93 ($id: expr) => {{
94 let bi = variant.bindings().into_iter().nth($id);
95 if let Some(bi) = bi {
96 bi.binding
97 } else {
98 panic!("Invalid index {} for {:?}", $id, variant.prefix);
99 }
100 }};
101 }
102 let args = iter.take_while(|arg| {
103 if let syn::NestedMeta::Meta(syn::Meta::NameValue(_)) = arg {
104 false
105 } else {
106 true
107 }
108 }).map(|arg| match arg {
109 syn::NestedMeta::Literal(syn::Lit::Int(int)) => {
110 let idx = int.value() as usize;
111 let bi = get_nth!(idx);
112 quote!(#bi)
113 }
114 syn::NestedMeta::Meta(syn::Meta::Word(ident)) => {
115 if ident.as_ref().starts_with("_") {
116 if let Ok(idx) = ident.as_ref()[1..].parse::<usize>() {
117 let bi = get_nth!(idx);
118 return quote!(#bi);
119 }
120 }
121 if let Some(bi) = variant
122 .bindings()
123 .into_iter()
124 .find(|bi| bi.ast().ident.as_ref() == Some(ident))
125 {
126 let bi = bi.binding;
127 quote!(#bi)
128 } else {
129 panic!(
130 "Invalid argument {} for {:?}",
131 ident.as_ref(),
132 variant.prefix
133 );
134 }
135 }
136 _ => panic!("Invalid argument to detailed=.."),
137 });
138 Some(quote!(format!(#s #(, #args)*)))
139}
140
141fn process_short(nested: &syn::NestedMeta) -> Option<quote::Tokens> {
142 match nested {
143 syn::NestedMeta::Meta(syn::Meta::NameValue(nameval)) => {
144 if nameval.ident == "detailed" {
145 return None;
146 }
147 if nameval.ident != "short" {
148 panic!("only short=.. or detailed=.. is allowed");
149 }
150 if let syn::Lit::Str(ref lit_str) = nameval.lit {
151 let val = lit_str.value();
152 Some(quote!(#val))
153 } else {
154 panic!("Oly string literal is allowed after short")
155 }
156 }
157 _ => None,
158 }
159}
160
161fn find_msg(attrs: &[syn::Attribute]) -> Option<syn::MetaList> {
162 let mut res = None;
163 for attr in attrs {
164 let meta = attr.interpret_meta();
165 if let Some(syn::Meta::List(list)) = meta {
166 if list.ident == "msg" {
167 if res.is_some() {
168 panic!("Cannot have multiple `msg` attributes");
169 }
170 res = Some(list);
171 }
172 }
173 }
174 res
175}