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
extern crate proc_macro; extern crate syn; #[macro_use] extern crate synstructure; #[macro_use] extern crate quote; decl_derive!([Display, attributes(display)] => display_derive); fn display_derive(s: synstructure::Structure) -> quote::Tokens { #[cfg(feature = "std")] let display = display_body(&s).map(|display_body| { s.bound_impl("::std::fmt::Display", quote! { #[allow(unreachable_code)] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { #display_body } ::std::result::Result::Ok(()) } }) }); #[cfg(not(feature = "std"))] let display = display_body(&s).map(|display_body| { s.bound_impl("::core::fmt::Display", quote! { #[allow(unreachable_code)] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match *self { #display_body } ::std::result::Result::Ok(()) } }) }); quote! { #display } } fn display_body(s: &synstructure::Structure) -> Option<quote::Tokens> { let mut msgs = s.variants().iter().map(|v| find_display_msg(&v.ast().attrs)); if msgs.all(|msg| msg.is_none()) { return None; } Some(s.each_variant(|v| { let msg = find_display_msg(&v.ast().attrs).expect("All variants must have display attribute."); if msg.is_empty() { panic!("Expected at least one argument to display attribute"); } let s = match msg[0] { syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref i, ref lit)) if i == "fmt" => { lit.clone() } _ => panic!("Display attribute must begin `fmt = \"\"` to control the Display message."), }; let args = msg[1..].iter().map(|arg| match *arg { syn::NestedMetaItem::Literal(syn::Lit::Int(i, _)) => { let bi = &v.bindings()[i as usize]; quote!(#bi) } syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref id)) => { if id.as_ref().starts_with("_") { if let Ok(idx) = id.as_ref()[1..].parse::<usize>() { let bi = &v.bindings()[idx]; return quote!(#bi) } } for bi in v.bindings() { if bi.ast().ident.as_ref() == Some(id) { return quote!(#bi); } } panic!("Couldn't find a field with this name!"); } _ => panic!("Invalid argument to display attribute!"), }); quote! { return write!(f, #s #(, #args)*) } })) } fn find_display_msg(attrs: &[syn::Attribute]) -> Option<&[syn::NestedMetaItem]> { let mut display_msg = None; for attr in attrs { if attr.name() == "display" { if display_msg.is_some() { panic!("Cannot have two display attributes") } else { if let syn::MetaItem::List(_, ref list) = attr.value { display_msg = Some(&list[..]); } else { panic!("Display attribute must take a list in parentheses") } } } } display_msg }