error_chain_mini_derive/
lib.rs

1//! derive for
2
3extern 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}