1 extern crate proc_macro2;
2 extern crate syn;
3
4 #[macro_use]
5 extern crate synstructure;
6 #[macro_use]
7 extern crate quote;
8
9 use proc_macro2::{TokenStream, Span};
10 use syn::LitStr;
11 use syn::spanned::Spanned;
12
13 #[derive(Debug)]
14 struct Error(TokenStream);
15
16 impl Error {
new(span: Span, message: &str) -> Error17 fn new(span: Span, message: &str) -> Error {
18 Error(quote_spanned! { span =>
19 compile_error!(#message);
20 })
21 }
22
into_tokens(self) -> TokenStream23 fn into_tokens(self) -> TokenStream {
24 self.0
25 }
26 }
27
28 decl_derive!([Fail, attributes(fail, cause)] => fail_derive);
29
fail_derive(s: synstructure::Structure) -> TokenStream30 fn fail_derive(s: synstructure::Structure) -> TokenStream {
31 match fail_derive_impl(s) {
32 Err(err) => err.into_tokens(),
33 Ok(tokens) => tokens,
34 }
35 }
36
fail_derive_impl(s: synstructure::Structure) -> Result<TokenStream, Error>37 fn fail_derive_impl(s: synstructure::Structure) -> Result<TokenStream, Error> {
38 let make_dyn = if cfg!(has_dyn_trait) {
39 quote! { &dyn }
40 } else {
41 quote! { & }
42 };
43
44 let ty_name = LitStr::new(&s.ast().ident.to_string(), Span::call_site());
45
46 let cause_body = s.each_variant(|v| {
47 if let Some(cause) = v.bindings().iter().find(is_cause) {
48 quote!(return Some(::failure::AsFail::as_fail(#cause)))
49 } else {
50 quote!(return None)
51 }
52 });
53
54 let bt_body = s.each_variant(|v| {
55 if let Some(bi) = v.bindings().iter().find(is_backtrace) {
56 quote!(return Some(#bi))
57 } else {
58 quote!(return None)
59 }
60 });
61
62 let fail = s.unbound_impl(
63 quote!(::failure::Fail),
64 quote! {
65 fn name(&self) -> Option<&str> {
66 Some(concat!(module_path!(), "::", #ty_name))
67 }
68
69 #[allow(unreachable_code)]
70 fn cause(&self) -> ::failure::_core::option::Option<#make_dyn(::failure::Fail)> {
71 match *self { #cause_body }
72 None
73 }
74
75 #[allow(unreachable_code)]
76 fn backtrace(&self) -> ::failure::_core::option::Option<&::failure::Backtrace> {
77 match *self { #bt_body }
78 None
79 }
80 },
81 );
82 let display = display_body(&s)?.map(|display_body| {
83 s.unbound_impl(
84 quote!(::failure::_core::fmt::Display),
85 quote! {
86 #[allow(unreachable_code)]
87 fn fmt(&self, f: &mut ::failure::_core::fmt::Formatter) -> ::failure::_core::fmt::Result {
88 match *self { #display_body }
89 write!(f, "An error has occurred.")
90 }
91 },
92 )
93 });
94
95 Ok(quote! {
96 #fail
97 #display
98 })
99 }
100
display_body(s: &synstructure::Structure) -> Result<Option<quote::__rt::TokenStream>, Error>101 fn display_body(s: &synstructure::Structure) -> Result<Option<quote::__rt::TokenStream>, Error> {
102 let mut msgs = s.variants().iter().map(|v| find_error_msg(&v.ast().attrs));
103 if msgs.all(|msg| msg.map(|m| m.is_none()).unwrap_or(true)) {
104 return Ok(None);
105 }
106
107 let mut tokens = TokenStream::new();
108 for v in s.variants() {
109 let msg =
110 find_error_msg(&v.ast().attrs)?
111 .ok_or_else(|| Error::new(
112 v.ast().ident.span(),
113 "All variants must have display attribute."
114 ))?;
115 if msg.nested.is_empty() {
116 return Err(Error::new(
117 msg.span(),
118 "Expected at least one argument to fail attribute"
119 ));
120 }
121
122 let format_string = match msg.nested[0] {
123 syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) if nv.ident == "display" => {
124 nv.lit.clone()
125 }
126 _ => {
127 return Err(Error::new(
128 msg.span(),
129 "Fail attribute must begin `display = \"\"` to control the Display message."
130 ));
131 }
132 };
133 let args = msg.nested.iter().skip(1).map(|arg| match *arg {
134 syn::NestedMeta::Literal(syn::Lit::Int(ref i)) => {
135 let bi = &v.bindings()[i.value() as usize];
136 Ok(quote!(#bi))
137 }
138 syn::NestedMeta::Meta(syn::Meta::Word(ref id)) => {
139 let id_s = id.to_string();
140 if id_s.starts_with("_") {
141 if let Ok(idx) = id_s[1..].parse::<usize>() {
142 let bi = match v.bindings().get(idx) {
143 Some(bi) => bi,
144 None => {
145 return Err(Error::new(
146 arg.span(),
147 &format!(
148 "display attempted to access field `{}` in `{}::{}` which \
149 does not exist (there are {} field{})",
150 idx,
151 s.ast().ident,
152 v.ast().ident,
153 v.bindings().len(),
154 if v.bindings().len() != 1 { "s" } else { "" }
155 )
156 ));
157 }
158 };
159 return Ok(quote!(#bi));
160 }
161 }
162 for bi in v.bindings() {
163 if bi.ast().ident.as_ref() == Some(id) {
164 return Ok(quote!(#bi));
165 }
166 }
167 return Err(Error::new(
168 arg.span(),
169 &format!(
170 "Couldn't find field `{}` in `{}::{}`",
171 id,
172 s.ast().ident,
173 v.ast().ident
174 )
175 ));
176 }
177 ref arg => {
178 return Err(Error::new(
179 arg.span(),
180 "Invalid argument to fail attribute!"
181 ));
182 },
183 });
184 let args = args.collect::<Result<Vec<_>, _>>()?;
185
186 let pat = v.pat();
187 tokens.extend(quote!(#pat => { return write!(f, #format_string #(, #args)*) }));
188 }
189 Ok(Some(tokens))
190 }
191
find_error_msg(attrs: &[syn::Attribute]) -> Result<Option<syn::MetaList>, Error>192 fn find_error_msg(attrs: &[syn::Attribute]) -> Result<Option<syn::MetaList>, Error> {
193 let mut error_msg = None;
194 for attr in attrs {
195 if let Some(meta) = attr.interpret_meta() {
196 if meta.name() == "fail" {
197 if error_msg.is_some() {
198 return Err(Error::new(
199 meta.span(),
200 "Cannot have two display attributes"
201 ));
202 } else {
203 if let syn::Meta::List(list) = meta {
204 error_msg = Some(list);
205 } else {
206 return Err(Error::new(
207 meta.span(),
208 "fail attribute must take a list in parentheses"
209 ));
210 }
211 }
212 }
213 }
214 }
215 Ok(error_msg)
216 }
217
is_backtrace(bi: &&synstructure::BindingInfo) -> bool218 fn is_backtrace(bi: &&synstructure::BindingInfo) -> bool {
219 match bi.ast().ty {
220 syn::Type::Path(syn::TypePath {
221 qself: None,
222 path: syn::Path {
223 segments: ref path, ..
224 },
225 }) => path.last().map_or(false, |s| {
226 s.value().ident == "Backtrace" && s.value().arguments.is_empty()
227 }),
228 _ => false,
229 }
230 }
231
is_cause(bi: &&synstructure::BindingInfo) -> bool232 fn is_cause(bi: &&synstructure::BindingInfo) -> bool {
233 let mut found_cause = false;
234 for attr in &bi.ast().attrs {
235 if let Some(meta) = attr.interpret_meta() {
236 if meta.name() == "cause" {
237 if found_cause {
238 panic!("Cannot have two `cause` attributes");
239 }
240 found_cause = true;
241 }
242 if meta.name() == "fail" {
243 if let syn::Meta::List(ref list) = meta {
244 if let Some(ref pair) = list.nested.first() {
245 if let &&syn::NestedMeta::Meta(syn::Meta::Word(ref word)) = pair.value() {
246 if word == "cause" {
247 if found_cause {
248 panic!("Cannot have two `cause` attributes");
249 }
250 found_cause = true;
251 }
252 }
253 }
254 }
255 }
256 }
257 }
258 found_cause
259 }
260