1extern crate proc_macro;
2extern crate syn;
3
4#[macro_use] extern crate synstructure;
5#[macro_use] extern crate quote;
6
7const DISPLAY_ATTR: &'static str = "display";
8const DISPLAY_MSG: &'static str = "msg";
9const CAUSE_ATTR: &'static str = "cause";
10
11decl_derive!([YadeError, attributes(display, cause)] => error_derive);
12fn error_derive(s: synstructure::Structure) -> quote::Tokens {
13 let display_impl = generate_display_impl(&s);
14 let error_impl = generate_error_impl(&s);
15
16 quote! {
17 #display_impl
18 #error_impl
19 }
20}
21
22decl_derive!([YadeKind, attributes(display)] => error_kind_derive);
23fn error_kind_derive(s: synstructure::Structure) -> quote::Tokens {
24 generate_display_impl(&s)
25}
26
27const INVALID_CAUSE: &'static str = "Cause must be a type implementing Error, a Box<Error>, or an Option<Box<Error>>";
28
29fn generate_error_impl(s: &synstructure::Structure) -> quote::Tokens {
30 let cause_matches = s.each_variant(|v| {
31 if let Some(cause) = v.bindings().iter().find(is_cause) {
32 let path = match cause.ast().ty {
33 syn::Ty::Path(_, ref path) => path,
34 _ => panic!(INVALID_CAUSE),
35 };
36 let parent_type = path.segments.last().unwrap();
37
38 match parent_type.ident.as_ref() {
39 "Option" => {
40 let child_path = match parent_type.parameters {
41 syn::PathParameters::AngleBracketed(ref data) => {
42 match data.types[0] {
43 syn::Ty::Path(_, ref path) => path,
44 _ => panic!(INVALID_CAUSE),
45 }
46 },
47 _ => panic!(INVALID_CAUSE),
48 };
49 let child_type = child_path.segments.last().unwrap();
50 match child_type.ident.as_ref() {
51 "Box" => {
52 quote!(return #cause.as_ref().map(|c|c.as_ref()))
53 },
54 _ => panic!(INVALID_CAUSE),
55 }
56
57 },
58 "Box" => quote!(return Some(#cause.as_ref())),
59 _ => quote!(return Some(#cause)),
60 }
61 } else {
62 quote!(return None)
63 }
64 });
65
66 s.bound_impl("::std::error::Error", quote! {
67 #[allow(unreachable_code)]
68 fn cause(&self) -> Option<&::std::error::Error> {
69 match *self { #cause_matches }
70 None
71 }
72
73 #[allow(unreachable_code)]
74 fn description(&self) -> &str {
75 "For a description please use the Display trait implementation of this Error"
76 }
77 })
78}
79
80fn generate_display_impl(s: &synstructure::Structure) -> quote::Tokens {
81 let display_matches = s.each_variant(|v| {
82 let ast = &v.ast();
83 let name = ast.ident.to_string();
84
85 let msg = match find_error_msg(&ast.attrs) {
86 Some(msg) => msg,
87 None => {
88 return quote! {
89 return write!(f, "{}", #name);
90 }
91 }
92 };
93
94 if msg.is_empty() {
95 panic!("Attribute '{}' must contain `{} = \"\"`", DISPLAY_ATTR, DISPLAY_MSG);
96 }
97
98 let s = match msg[0] {
99 syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref i, ref lit)) if i == DISPLAY_MSG => {
100 lit.clone()
101 }
102 _ => panic!("Attribute '{}' must contain `{} = \"\"`", DISPLAY_ATTR, DISPLAY_MSG),
103 };
104 use quote::ToTokens;
105 let args = msg[1..].iter().map(|arg| match *arg {
106 syn::NestedMetaItem::Literal(syn::Lit::Int(i, _)) => {
107 let bi = &v.bindings()[i as usize];
108 quote!(#bi)
109 }
110 syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref id)) => {
111 if id.as_ref().starts_with("_") {
112 if let Ok(idx) = id.as_ref()[1..].parse::<usize>() {
113 let bi = &v.bindings()[idx];
114 return quote!(#bi)
115 }
116 }
117 for bi in v.bindings() {
118 if bi.ast().ident.as_ref() == Some(id) {
119 return quote!(#bi);
120 }
121 }
122 panic!("Couldn't find field '{}' of '{}'", id, name);
123 }
124 _ => {
125 let mut tokens = quote::Tokens::new();
126 arg.to_tokens(&mut tokens);
127 panic!("Invalid '{}' attribute argument `{}` for '{}'", DISPLAY_ATTR, tokens, name);
128 },
129 });
130
131 quote! {
132 return write!(f, #s #(, #args)*)
133 }
134 });
135
136 s.bound_impl("::std::fmt::Display", quote! {
137 #[allow(unreachable_code)]
138 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
139 match *self { #display_matches }
140 write!(f, "There was an unknown error.")
141 }
142 })
143}
144
145fn find_error_msg(attrs: &[syn::Attribute]) -> Option<&[syn::NestedMetaItem]> {
146 let mut error_msg = None;
147
148 for attr in attrs {
149 if attr.name() == DISPLAY_ATTR {
150 if error_msg.is_some() {
151 panic!("Attribute '{}' specified multiple times.", DISPLAY_ATTR)
152 } else {
153 if let syn::MetaItem::List(_, ref list) = attr.value {
154 error_msg = Some(&list[..]);
155 } else {
156 panic!("Attribute '{}' requires parens", DISPLAY_ATTR)
157 }
158 }
159 }
160 }
161
162 error_msg
163}
164
165fn is_cause(bi: &&synstructure::BindingInfo) -> bool {
166 bi.ast().attrs.iter().any(|attr| attr.name() == CAUSE_ATTR)
167}