diff --git a/strum_macros/Cargo.toml b/strum_macros/Cargo.toml index 9c6879d3..cf1154fc 100644 --- a/strum_macros/Cargo.toml +++ b/strum_macros/Cargo.toml @@ -17,5 +17,5 @@ proc-macro = true name = "strum_macros" [dependencies] -quote = "0.3.12" -syn = "0.11.4" +quote = "0.5.2" +syn = { version = "0.13.7", features = ["parsing"] } diff --git a/strum_macros/src/as_ref_str.rs b/strum_macros/src/as_ref_str.rs index e0603472..f355956f 100644 --- a/strum_macros/src/as_ref_str.rs +++ b/strum_macros/src/as_ref_str.rs @@ -2,45 +2,46 @@ use quote; use syn; -use helpers::{unique_attr, extract_attrs, is_disabled}; +use helpers::{unique_attr, extract_attrs, extract_meta, is_disabled}; pub fn as_ref_str_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("AsRefStr only works on Enums"), }; let mut arms = Vec::new(); for variant in variants { - use syn::VariantData::*; + use syn::Fields::*; let ident = &variant.ident; + let meta = extract_meta(&variant.attrs); - if is_disabled(&variant.attrs) { + if is_disabled(&meta) { continue; } // Look at all the serialize attributes. // Use `to_string` attribute (not `as_ref_str` or something) to keep things consistent // (i.e. always `enum.as_ref().to_string() == enum.to_string()`). - let output = if let Some(n) = unique_attr(&variant.attrs, "strum", "to_string") { + let output = if let Some(n) = unique_attr(&meta, "strum", "to_string") { n } else { - let mut attrs = extract_attrs(&variant.attrs, "strum", "serialize"); + let mut attrs = extract_attrs(&meta, "strum", "serialize"); // We always take the longest one. This is arbitary, but is *mostly* deterministic - attrs.sort_by_key(|s| -(s.len() as isize)); - if let Some(n) = attrs.first() { + attrs.sort_by_key(|s| s.len()); + if let Some(n) = attrs.pop() { n } else { - ident.as_ref() + ident.to_string() } }; - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(..) => quote::Ident::from("(..)"), - Struct(..) => quote::Ident::from("{..}"), + let params = match variant.fields { + Unit => quote!{}, + Unnamed(..) => quote!{ (..) }, + Named(..) => quote!{ {..} }, }; arms.push(quote!{ #name::#ident #params => #output }); diff --git a/strum_macros/src/display.rs b/strum_macros/src/display.rs index 0d6c3b7b..73d247bc 100644 --- a/strum_macros/src/display.rs +++ b/strum_macros/src/display.rs @@ -2,43 +2,44 @@ use quote; use syn; -use helpers::{unique_attr, extract_attrs, is_disabled}; +use helpers::{unique_attr, extract_attrs, extract_meta, is_disabled}; pub fn display_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("Display only works on Enums"), }; let mut arms = Vec::new(); for variant in variants { - use syn::VariantData::*; + use syn::Fields::*; let ident = &variant.ident; + let meta = extract_meta(&variant.attrs); - if is_disabled(&variant.attrs) { + if is_disabled(&meta) { continue; } // Look at all the serialize attributes. - let output = if let Some(n) = unique_attr(&variant.attrs, "strum", "to_string") { + let output = if let Some(n) = unique_attr(&meta, "strum", "to_string") { n } else { - let mut attrs = extract_attrs(&variant.attrs, "strum", "serialize"); + let mut attrs = extract_attrs(&meta, "strum", "serialize"); // We always take the longest one. This is arbitary, but is *mostly* deterministic - attrs.sort_by_key(|s| -(s.len() as isize)); - if let Some(n) = attrs.first() { + attrs.sort_by_key(|s| s.len()); + if let Some(n) = attrs.pop() { n } else { - ident.as_ref() + ident.to_string() } }; - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(..) => quote::Ident::from("(..)"), - Struct(..) => quote::Ident::from("{..}"), + let params = match variant.fields { + Unit => quote!{}, + Unnamed(..) => quote!{ (..) }, + Named(..) => quote!{ {..} }, }; arms.push(quote!{ #name::#ident #params => f.write_str(#output) }); diff --git a/strum_macros/src/enum_iter.rs b/strum_macros/src/enum_iter.rs index 1e4fd7ca..2fd58ac1 100644 --- a/strum_macros/src/enum_iter.rs +++ b/strum_macros/src/enum_iter.rs @@ -1,7 +1,7 @@ use quote; use syn; -use helpers::is_disabled; +use helpers::{extract_meta, is_disabled}; pub fn enum_iter_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; @@ -9,57 +9,41 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> quote::Tokens { let (impl_generics, ty_generics, where_clause) = gen.split_for_impl(); let vis = &ast.vis; - if gen.lifetimes.len() > 0 { + if gen.lifetimes().count() > 0 { panic!("Enum Iterator isn't supported on Enums with lifetimes. The resulting enums would \ be unbounded."); } - let phantom_data = if gen.ty_params.len() > 0 { - let g = gen.ty_params - .iter() - .map(|param| ¶m.ident) - .collect::>(); - quote!{ < ( #(#g),* ) > } + let phantom_data = if gen.type_params().count() > 0 { + let g = gen.type_params().map(|param| ¶m.ident); + quote! { < ( #(#g),* ) > } } else { quote! { < () > } }; - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumIter only works on Enums"), }; let mut arms = Vec::new(); let enabled = variants .iter() - .filter(|variant| !is_disabled(&variant.attrs)); + .filter(|variant| !is_disabled(&extract_meta(&variant.attrs))); for (idx, variant) in enabled.enumerate() { - use syn::VariantData::*; + use syn::Fields::*; let ident = &variant.ident; - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(ref fields) => { - let default = fields - .iter() - .map(|_| "::std::default::Default::default()") - .collect::>() - .join(", "); - - quote::Ident::from(&*format!("({})", default)) + let params = match variant.fields { + Unit => quote!{}, + Unnamed(ref fields) => { + let defaults = ::std::iter::repeat(quote!(::std::default::Default::default())) + .take(fields.unnamed.len()); + quote! { (#(#defaults),*) } } - Struct(ref fields) => { - let default = fields - .iter() - .map(|field| { - format!("{}: {}", - field.ident.as_ref().unwrap(), - "::std::default::Default::default()") - }) - .collect::>() - .join(", "); - - quote::Ident::from(&*format!("{{{}}}", default)) + Named(ref fields) => { + let fields = fields.named.iter().map(|field| field.ident.unwrap()); + quote! { {#(#fields: ::std::default::Default::default()),*} } } }; @@ -68,7 +52,7 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> quote::Tokens { let variant_count = arms.len(); arms.push(quote! { _ => ::std::option::Option::None }); - let iter_name = quote::Ident::from(&*format!("{}Iter", name)); + let iter_name = syn::parse_str::(&format!("{}Iter", name)).unwrap(); quote!{ #vis struct #iter_name #ty_generics { idx: usize, @@ -84,10 +68,10 @@ pub fn enum_iter_inner(ast: &syn::DeriveInput) -> quote::Tokens { } } } - + impl #impl_generics Iterator for #iter_name #ty_generics #where_clause { type Item = #name #ty_generics; - + fn next(&mut self) -> Option<#name #ty_generics> { let output = match self.idx { #(#arms),* diff --git a/strum_macros/src/enum_messages.rs b/strum_macros/src/enum_messages.rs index f195fbe0..81ccf045 100644 --- a/strum_macros/src/enum_messages.rs +++ b/strum_macros/src/enum_messages.rs @@ -1,13 +1,13 @@ use quote; use syn; -use helpers::{unique_attr, extract_attrs, is_disabled}; +use helpers::{unique_attr, extract_attrs, extract_meta, is_disabled}; pub fn enum_message_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumMessage only works on Enums"), }; @@ -16,22 +16,23 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> quote::Tokens { let mut serializations = Vec::new(); for variant in variants { - let messages = unique_attr(&variant.attrs, "strum", "message"); - let detailed_messages = unique_attr(&variant.attrs, "strum", "detailed_message"); + let meta = extract_meta(&variant.attrs); + let messages = unique_attr(&meta, "strum", "message"); + let detailed_messages = unique_attr(&meta, "strum", "detailed_message"); let ident = &variant.ident; - use syn::VariantData::*; - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(..) => quote::Ident::from("(..)"), - Struct(..) => quote::Ident::from("{..}"), + use syn::Fields::*; + let params = match variant.fields { + Unit => quote!{}, + Unnamed(..) => quote!{ (..) }, + Named(..) => quote!{ {..} }, }; // You can't disable getting the serializations. { - let mut serialization_variants = extract_attrs(&variant.attrs, "strum", "serialize"); + let mut serialization_variants = extract_attrs(&meta, "strum", "serialize"); if serialization_variants.len() == 0 { - serialization_variants.push(ident.as_ref()); + serialization_variants.push(ident.to_string()); } let count = serialization_variants.len(); @@ -44,7 +45,7 @@ pub fn enum_message_inner(ast: &syn::DeriveInput) -> quote::Tokens { } // But you can disable the messages. - if is_disabled(&variant.attrs) { + if is_disabled(&meta) { continue; } diff --git a/strum_macros/src/enum_properties.rs b/strum_macros/src/enum_properties.rs index abf63bf7..32df50dd 100644 --- a/strum_macros/src/enum_properties.rs +++ b/strum_macros/src/enum_properties.rs @@ -1,41 +1,40 @@ use quote; use syn; +use syn::Meta; -use helpers::is_disabled; +use helpers::{extract_meta, is_disabled}; -fn extract_properties(ast: &syn::Variant) -> Vec<(&syn::Ident, &syn::Lit)> { - use syn::*; - ast.attrs - .iter() - .filter_map(|attr| { - // Look for all the strum attributes - if let &Attribute { value: MetaItem::List(ref ident, ref nested), .. } = attr { +fn extract_properties(meta: &[Meta]) -> Vec<(&syn::Ident, &syn::Lit)> { + use syn::{MetaList, MetaNameValue, NestedMeta}; + meta.iter() + .filter_map(|meta| match *meta { + Meta::List(MetaList { ref ident, ref nested, .. }) => { if ident == "strum" { - return Option::Some(nested); + Some(nested) + } else { + None } - } - - Option::None + }, + _ => None, }) .flat_map(|prop| prop) - .filter_map(|prop| { - // Look for all the recursive property attributes - if let &NestedMetaItem::MetaItem(MetaItem::List(ref ident, ref nested)) = prop { + .filter_map(|prop| match *prop { + NestedMeta::Meta(Meta::List(MetaList { ref ident, ref nested, .. })) => { if ident == "props" { - return Option::Some(nested); + Some(nested) + } else { + None } - } - - Option::None + }, + _ => None, }) .flat_map(|prop| prop) - .filter_map(|prop| { - // Only look at key value pairs - if let &NestedMetaItem::MetaItem(MetaItem::NameValue(ref ident, ref value)) = prop { - return Option::Some((ident, value)); - } - - Option::None + // Only look at key value pairs + .filter_map(|prop| match *prop { + NestedMeta::Meta(Meta::NameValue(MetaNameValue { ref ident, ref lit, .. })) => { + Some((ident, lit)) + }, + _ => None, }) .collect() } @@ -43,33 +42,34 @@ fn extract_properties(ast: &syn::Variant) -> Vec<(&syn::Ident, &syn::Lit)> { pub fn enum_properties_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumProp only works on Enums"), }; let mut arms = Vec::new(); for variant in variants { let ident = &variant.ident; + let meta = extract_meta(&variant.attrs); let mut string_arms = Vec::new(); let mut bool_arms = Vec::new(); let mut num_arms = Vec::new(); // But you can disable the messages. - if is_disabled(&variant.attrs) { + if is_disabled(&meta) { continue; } - use syn::VariantData::*; - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(..) => quote::Ident::from("(..)"), - Struct(..) => quote::Ident::from("{..}"), + use syn::Fields::*; + let params = match variant.fields { + Unit => quote!{}, + Unnamed(..) => quote!{ (..) }, + Named(..) => quote!{ {..} }, }; - for (key, value) in extract_properties(&variant) { + for (key, value) in extract_properties(&meta) { use syn::Lit::*; let key = key.as_ref(); - match *value { + match value { Str(ref s, ..) => { string_arms.push(quote!{ #key => ::std::option::Option::Some( #s )}) } diff --git a/strum_macros/src/from_string.rs b/strum_macros/src/from_string.rs index 668ba605..e87f24c1 100644 --- a/strum_macros/src/from_string.rs +++ b/strum_macros/src/from_string.rs @@ -2,13 +2,13 @@ use quote; use syn; -use helpers::{unique_attr, extract_attrs, is_disabled}; +use helpers::{unique_attr, extract_attrs, extract_meta, is_disabled}; pub fn from_string_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("FromString only works on Enums"), }; @@ -17,23 +17,24 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> quote::Tokens { quote! { _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound) }; let mut arms = Vec::new(); for variant in variants { - use syn::VariantData::*; + use syn::Fields::*; let ident = &variant.ident; + let meta = extract_meta(&variant.attrs); // Look at all the serialize attributes. - let mut attrs = extract_attrs(&variant.attrs, "strum", "serialize"); - attrs.extend(extract_attrs(&variant.attrs, "strum", "to_string")); - if is_disabled(&variant.attrs) { + let mut attrs = extract_attrs(&meta, "strum", "serialize"); + attrs.extend(extract_attrs(&meta, "strum", "to_string")); + if is_disabled(&meta) { continue; } - if let Some("true") = unique_attr(&variant.attrs, "strum", "default") { + if unique_attr(&meta, "strum", "default").map_or(false, |s| s == "true") { if has_default { panic!("Can't have multiple default variants"); } - if let Tuple(ref fields) = variant.data { - if fields.len() != 1 { + if let Unnamed(ref fields) = variant.fields { + if fields.unnamed.len() != 1 { panic!("Default only works on unit structs with a single String parameter"); } @@ -50,30 +51,19 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> quote::Tokens { // If we don't have any custom variants, add the default name. if attrs.len() == 0 { - attrs.push(ident.as_ref()); + attrs.push(ident.to_string()); } - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(ref fields) => { - let default = fields - .iter() - .map(|_| "Default::default()") - .collect::>() - .join(", "); - - quote::Ident::from(&*format!("({})", default)) + let params = match variant.fields { + Unit => quote!{}, + Unnamed(ref fields) => { + let defaults = ::std::iter::repeat(quote!(Default::default())) + .take(fields.unnamed.len()); + quote! { (#(#defaults),*) } } - Struct(ref fields) => { - let default = fields - .iter() - .map(|field| { - format!("{}:{}", field.ident.as_ref().unwrap(), "Default::default()") - }) - .collect::>() - .join(", "); - - quote::Ident::from(&*format!("{{{}}}", default)) + Named(ref fields) => { + let fields = fields.named.iter().map(|field| field.ident.unwrap()); + quote! { {#(#fields: Default::default()),*} } } }; @@ -92,4 +82,4 @@ pub fn from_string_inner(ast: &syn::DeriveInput) -> quote::Tokens { } } } -} \ No newline at end of file +} diff --git a/strum_macros/src/helpers.rs b/strum_macros/src/helpers.rs index c32e8392..b56a6d2c 100644 --- a/strum_macros/src/helpers.rs +++ b/strum_macros/src/helpers.rs @@ -1,32 +1,46 @@ -use syn; -use syn::Attribute; +use syn::{Attribute, Meta}; -pub fn extract_attrs<'a>(attrs: &'a [Attribute], attr: &str, prop: &str) -> Vec<&'a str> { +pub fn extract_meta(attrs: &[Attribute]) -> Vec { attrs.iter() + .filter_map(|attribute| attribute.interpret_meta()) + .collect() +} + +pub fn extract_attrs(meta: &[Meta], attr: &str, prop: &str) -> Vec { + use syn::{Lit, MetaNameValue, NestedMeta}; + meta.iter() // Get all the attributes with our tag on them. - .filter_map(|attribute| { - use syn::MetaItem::*; - if let List(ref i, ref nested) = attribute.value { - if i == attr { Some(nested) } else { None } - } else { - None - } + .filter_map(|meta| match *meta { + Meta::List(ref metalist) => { + if metalist.ident == attr { + Some(&metalist.nested) + } else { + None + } + }, + _ => None, }) .flat_map(|nested| nested) // Get all the inner elements as long as they start with ser. - .filter_map(|attribute| { - use syn::NestedMetaItem::*; - use syn::MetaItem::*; - if let &MetaItem(NameValue(ref i, syn::Lit::Str(ref s, ..))) = attribute { - if i == prop { Some(&**s) } else { None } - } else { - None - } - }).collect() + .filter_map(|meta| match *meta { + NestedMeta::Meta(Meta::NameValue(MetaNameValue { + ref ident, + lit: Lit::Str(ref s), + .. + })) => { + if ident == prop { + Some(s.value()) + } else { + None + } + }, + _ => None, + }) + .collect() } -pub fn unique_attr<'a>(attrs: &'a [Attribute], attr: &str, prop: &str) -> Option<&'a str> { +pub fn unique_attr(attrs: &[Meta], attr: &str, prop: &str) -> Option { let mut curr = extract_attrs(attrs, attr, prop); if curr.len() > 1 { panic!("More than one property: {} found on variant", prop); @@ -35,11 +49,11 @@ pub fn unique_attr<'a>(attrs: &'a [Attribute], attr: &str, prop: &str) -> Option curr.pop() } -pub fn is_disabled(attrs: &[Attribute]) -> bool { +pub fn is_disabled(attrs: &[Meta]) -> bool { let v = extract_attrs(attrs, "strum", "disabled"); match v.len() { 0 => false, 1 => v[0] == "true", _ => panic!("Can't have multiple values for 'disabled'"), } -} \ No newline at end of file +} diff --git a/strum_macros/src/lib.rs b/strum_macros/src/lib.rs index 0afb9a68..581afac2 100644 --- a/strum_macros/src/lib.rs +++ b/strum_macros/src/lib.rs @@ -42,70 +42,63 @@ fn debug_print_generated(ast: &syn::DeriveInput, toks: "e::Tokens) { #[proc_macro_derive(EnumString,attributes(strum))] pub fn from_string(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = from_string::from_string_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } #[proc_macro_derive(AsRefStr,attributes(strum))] pub fn as_ref_str(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = as_ref_str::as_ref_str_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } #[proc_macro_derive(ToString,attributes(strum))] pub fn to_string(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = to_string::to_string_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } #[proc_macro_derive(Display,attributes(strum))] pub fn display(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = display::display_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } #[proc_macro_derive(EnumIter,attributes(strum))] pub fn enum_iter(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = enum_iter::enum_iter_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } #[proc_macro_derive(EnumMessage,attributes(strum))] pub fn enum_messages(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = enum_messages::enum_message_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } #[proc_macro_derive(EnumProperty,attributes(strum))] pub fn enum_properties(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); + let ast = syn::parse(input).unwrap(); let toks = enum_properties::enum_properties_inner(&ast); debug_print_generated(&ast, &toks); - toks.parse().unwrap() + toks.into() } diff --git a/strum_macros/src/to_string.rs b/strum_macros/src/to_string.rs index 8eae6796..101a0fb7 100644 --- a/strum_macros/src/to_string.rs +++ b/strum_macros/src/to_string.rs @@ -2,43 +2,44 @@ use quote; use syn; -use helpers::{unique_attr, extract_attrs, is_disabled}; +use helpers::{unique_attr, extract_attrs, extract_meta, is_disabled}; pub fn to_string_inner(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - let variants = match ast.body { - syn::Body::Enum(ref v) => v, + let variants = match ast.data { + syn::Data::Enum(ref v) => &v.variants, _ => panic!("ToString only works on Enums"), }; let mut arms = Vec::new(); for variant in variants { - use syn::VariantData::*; + use syn::Fields::*; let ident = &variant.ident; + let meta = extract_meta(&variant.attrs); - if is_disabled(&variant.attrs) { + if is_disabled(&meta) { continue; } // Look at all the serialize attributes. - let output = if let Some(n) = unique_attr(&variant.attrs, "strum", "to_string") { + let output = if let Some(n) = unique_attr(&meta, "strum", "to_string") { n } else { - let mut attrs = extract_attrs(&variant.attrs, "strum", "serialize"); + let mut attrs = extract_attrs(&meta, "strum", "serialize"); // We always take the longest one. This is arbitary, but is *mostly* deterministic - attrs.sort_by_key(|s| -(s.len() as isize)); - if let Some(n) = attrs.first() { + attrs.sort_by_key(|s| s.len()); + if let Some(n) = attrs.pop() { n } else { - ident.as_ref() + ident.to_string() } }; - let params = match variant.data { - Unit => quote::Ident::from(""), - Tuple(..) => quote::Ident::from("(..)"), - Struct(..) => quote::Ident::from("{..}"), + let params = match variant.fields { + Unit => quote!{}, + Unnamed(..) => quote!{ (..) }, + Named(..) => quote!{ {..} }, }; arms.push(quote!{ #name::#ident #params => ::std::string::String::from(#output) });