strum_macros-0.15.0/Cargo.toml.orig000064400000000000000000000017211344352003000153630ustar0000000000000000[package] name = "strum_macros" version = "0.15.0" authors = ["Peter Glotfelty "] license = "MIT" description = "Helpful macros for working with enums and strings" keywords = ["enum", "string", "macros", "proc-macros"] categories = ["development-tools::procedural-macro-helpers", "parsing"] documentation = "https://docs.rs/strum" homepage = "https://github.com/Peternator7/strum" readme = "../README.md" [lib] proc-macro = true name = "strum_macros" [dependencies] heck = "0.3" proc-macro2 = "0.4" quote = "0.6" syn = { version = "0.15", features = ["parsing", "extra-traits"] } [features] verbose-enumstring-name = [] verbose-asrefstr-name = [] verbose-asstaticstr-name = [] verbose-intostaticstr-name = [] verbose-tostring-name = [] verbose-display-name = [] verbose-enumiter-name = [] verbose-enummessage-name = [] verbose-enumproperty-name = [] verbose-enumdiscriminants-name = [] verbose-enumcount-name = []strum_macros-0.15.0/Cargo.toml0000644000000027710000000000000117010ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "strum_macros" version = "0.15.0" authors = ["Peter Glotfelty "] description = "Helpful macros for working with enums and strings" homepage = "https://github.com/Peternator7/strum" documentation = "https://docs.rs/strum" readme = "../README.md" keywords = ["enum", "string", "macros", "proc-macros"] categories = ["development-tools::procedural-macro-helpers", "parsing"] license = "MIT" [lib] name = "strum_macros" proc-macro = true [dependencies.heck] version = "0.3" [dependencies.proc-macro2] version = "0.4" [dependencies.quote] version = "0.6" [dependencies.syn] version = "0.15" features = ["parsing", "extra-traits"] [features] verbose-asrefstr-name = [] verbose-asstaticstr-name = [] verbose-display-name = [] verbose-enumcount-name = [] verbose-enumdiscriminants-name = [] verbose-enumiter-name = [] verbose-enummessage-name = [] verbose-enumproperty-name = [] verbose-enumstring-name = [] verbose-intostaticstr-name = [] verbose-tostring-name = [] strum_macros-0.15.0/src/as_ref_str.rs000064400000000000000000000103551343300667500160000ustar0000000000000000use proc_macro2::TokenStream; use syn; use case_style::CaseStyle; use helpers::{convert_case, extract_attrs, extract_meta, is_disabled, unique_attr}; fn get_arms(ast: &syn::DeriveInput) -> Vec { let name = &ast.ident; let mut arms = Vec::new(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("This macro only works on Enums"), }; let type_meta = extract_meta(&ast.attrs); let case_style = unique_attr(&type_meta, "strum", "serialize_all") .map(|style| CaseStyle::from(style.as_ref())); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let meta = extract_meta(&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(&meta, "strum", "to_string") { n } else { 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()); if let Some(n) = attrs.pop() { n } else { convert_case(ident, case_style) } }; let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => #output }); } if arms.len() < variants.len() { arms.push(quote! { _ => panic!("AsRef::::as_ref() or AsStaticRef::::as_static() \ called on disabled variant.") }) } arms } pub fn as_ref_str_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let arms = get_arms(ast); quote! { impl #impl_generics ::std::convert::AsRef for #name #ty_generics #where_clause { fn as_ref(&self) -> &str { match *self { #(#arms),* } } } } } pub enum GenerateTraitVariant { AsStaticStr, From, } pub fn as_static_str_inner( ast: &syn::DeriveInput, trait_variant: GenerateTraitVariant, ) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let arms = get_arms(ast); let mut generics = ast.generics.clone(); generics .params .push(syn::GenericParam::Lifetime(syn::LifetimeDef::new( parse_quote!('_derivative_strum), ))); let (impl_generics2, _, _) = generics.split_for_impl(); let arms2 = arms.clone(); let arms3 = arms.clone(); match trait_variant { GenerateTraitVariant::AsStaticStr => { quote! { impl #impl_generics ::strum::AsStaticRef for #name #ty_generics #where_clause { fn as_static(&self) -> &'static str { match *self { #(#arms),* } } } } } GenerateTraitVariant::From => { quote! { impl #impl_generics ::std::convert::From<#name #ty_generics> for &'static str #where_clause { fn from(x: #name #ty_generics) -> &'static str { match x { #(#arms2),* } } } impl #impl_generics2 ::std::convert::From<&'_derivative_strum #name #ty_generics> for &'static str #where_clause { fn from(x: &'_derivative_strum #name #ty_generics) -> &'static str { match *x { #(#arms3),* } } } } } } } strum_macros-0.15.0/src/case_style.rs000064400000000000000000000020521335276225000157750ustar0000000000000000#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CaseStyle { CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase, } impl<'s> From<&'s str> for CaseStyle { fn from(text: &'s str) -> CaseStyle { match text { "camel_case" => CaseStyle::CamelCase, "kebab_case" => CaseStyle::KebabCase, "mixed_case" => CaseStyle::MixedCase, "shouty_snake_case" | "shouty_snek_case" => CaseStyle::ShoutySnakeCase, "snake_case" | "snek_case" => CaseStyle::SnakeCase, "title_case" => CaseStyle::TitleCase, _ => panic!( "Unexpected case style for serialize_all: `{}`. Valid values are: `{:?}`", text, [ "camel_case", "kebab_case", "mixed_case", "shouty_snake_case", "snake_case", "title_case" ] ), } } } strum_macros-0.15.0/src/display.rs000064400000000000000000000041731343715223200153120ustar0000000000000000use proc_macro2::TokenStream; use syn; use case_style::CaseStyle; use helpers::{convert_case, extract_attrs, extract_meta, is_disabled, unique_attr}; pub fn display_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("Display only works on Enums"), }; let type_meta = extract_meta(&ast.attrs); let case_style = unique_attr(&type_meta, "strum", "serialize_all") .map(|style| CaseStyle::from(style.as_ref())); let mut arms = Vec::new(); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let meta = extract_meta(&variant.attrs); if is_disabled(&meta) { continue; } // Look at all the serialize attributes. let output = if let Some(n) = unique_attr(&meta, "strum", "to_string") { n } else { 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()); if let Some(n) = attrs.pop() { n } else { convert_case(ident, case_style) } }; let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => f.write_str(#output) }); } if arms.len() < variants.len() { arms.push(quote! { _ => panic!("fmt() called on disabled variant.")}) } quote! { impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { match *self { #(#arms),* } } } } } strum_macros-0.15.0/src/enum_count.rs000064400000000000000000000017731343715223200160240ustar0000000000000000use proc_macro2::{Span, TokenStream}; use syn; pub(crate) fn enum_count_inner(ast: &syn::DeriveInput) -> TokenStream { let n = match ast.data { syn::Data::Enum(ref v) => v.variants.len(), _ => panic!("EnumCount can only be used with enums"), }; // Used in the quasi-quotation below as `#name` let name = &ast.ident; let const_name = &syn::Ident::new( &format!("{}_COUNT", name.to_string().to_uppercase()), Span::call_site(), ); // Helper is provided for handling complex generic types correctly and effortlessly let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); quote! { // Implementation impl #impl_generics ::strum::EnumCount for #name #ty_generics #where_clause { fn count() -> usize { #n } } #[allow(dead_code, missing_docs)] pub const #const_name: usize = #n; } } strum_macros-0.15.0/src/enum_discriminants.rs000064400000000000000000000117741343715223200175450ustar0000000000000000use proc_macro2::{Span, TokenStream}; use syn; use helpers::{ extract_list_metas, extract_meta, filter_metas, get_meta_ident, get_meta_list, unique_meta_list, }; pub fn enum_discriminants_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let vis = &ast.vis; let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumDiscriminants only works on Enums"), }; // Derives for the generated enum let type_meta = extract_meta(&ast.attrs); let discriminant_attrs = get_meta_list(type_meta.iter(), "strum_discriminants") .flat_map(|meta| extract_list_metas(meta).collect::>()) .collect::>(); let derives = get_meta_list(discriminant_attrs.iter().map(|&m| m), "derive") .flat_map(extract_list_metas) .filter_map(get_meta_ident) .collect::>(); let derives = quote! { #[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)] }; // Work out the name let default_name = syn::Ident::new( &format!("{}Discriminants", name.to_string()), Span::call_site(), ); let discriminants_name = unique_meta_list(discriminant_attrs.iter().map(|&m| m), "name") .map(extract_list_metas) .and_then(|metas| metas.filter_map(get_meta_ident).next()) .unwrap_or(&default_name); // Pass through all other attributes let pass_though_attributes = filter_metas(discriminant_attrs.iter().map(|&m| m), |meta| match meta { syn::Meta::List(ref metalist) => metalist.ident != "derive" && metalist.ident != "name", _ => true, }) .map(|meta| quote! { #[ #meta ] }) .collect::>(); // Add the variants without fields, but exclude the `strum` meta item let mut discriminants = Vec::new(); for variant in variants { let ident = &variant.ident; // Don't copy across the "strum" meta attribute. let attrs = variant.attrs.iter().filter(|attr| { attr.interpret_meta().map_or(true, |meta| match meta { syn::Meta::List(ref metalist) => metalist.ident != "strum", _ => true, }) }); discriminants.push(quote! { #(#attrs)* #ident }); } // Ideally: // // * For `Copy` types, we `impl From for TheEnumDiscriminants` // * For `!Copy` types, we `impl<'enum> From<&'enum TheEnum> for TheEnumDiscriminants` // // That way we ensure users are not able to pass a `Copy` type by reference. However, the // `#[derive(..)]` attributes are not in the parsed tokens, so we are not able to check if a // type is `Copy`, so we just implement both. // // See // --- // let is_copy = unique_meta_list(type_meta.iter(), "derive") // .map(extract_list_metas) // .map(|metas| { // metas // .filter_map(get_meta_ident) // .any(|derive| derive.to_string() == "Copy") // }).unwrap_or(false); let arms = variants .iter() .map(|variant| { let ident = &variant.ident; use syn::Fields::*; let params = match variant.fields { Unit => quote! {}, Unnamed(ref _fields) => { quote! { (..) } } Named(ref _fields) => { quote! { { .. } } } }; quote! { #name::#ident #params => #discriminants_name::#ident } }) .collect::>(); let from_fn_body = quote! { match val { #(#arms),* } }; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let impl_from = quote! { impl #impl_generics ::std::convert::From< #name #ty_generics > for #discriminants_name #where_clause { fn from(val: #name #ty_generics) -> #discriminants_name { #from_fn_body } } }; let impl_from_ref = { let mut generics = ast.generics.clone(); let lifetime = parse_quote!('_enum); let enum_life = quote! { & #lifetime }; generics.params.push(lifetime); // Shadows the earlier `impl_generics` let (impl_generics, _, _) = generics.split_for_impl(); quote! { impl #impl_generics ::std::convert::From< #enum_life #name #ty_generics > for #discriminants_name #where_clause { fn from(val: #enum_life #name #ty_generics) -> #discriminants_name { #from_fn_body } } } }; quote! { /// Auto-generated discriminant enum variants #derives #(#pass_though_attributes)* #vis enum #discriminants_name { #(#discriminants),* } #impl_from #impl_from_ref } } strum_macros-0.15.0/src/enum_iter.rs000064400000000000000000000070721343715223200156350ustar0000000000000000use proc_macro2::TokenStream; use syn; use helpers::{extract_meta, is_disabled}; pub fn enum_iter_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let gen = &ast.generics; let (impl_generics, ty_generics, where_clause) = gen.split_for_impl(); let vis = &ast.vis; 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.type_params().count() > 0 { let g = gen.type_params().map(|param| ¶m.ident); quote! { < ( #(#g),* ) > } } else { quote! { < () > } }; 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(&extract_meta(&variant.attrs))); for (idx, variant) in enabled.enumerate() { use syn::Fields::*; let ident = &variant.ident; 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),*) } } Named(ref fields) => { let fields = fields .named .iter() .map(|field| field.ident.as_ref().unwrap()); quote! { {#(#fields: ::std::default::Default::default()),*} } } }; arms.push(quote! {#idx => ::std::option::Option::Some(#name::#ident #params)}); } let variant_count = arms.len(); arms.push(quote! { _ => ::std::option::Option::None }); let iter_name = syn::parse_str::(&format!("{}Iter", name)).unwrap(); quote! { #[allow(missing_docs)] #vis struct #iter_name #ty_generics { idx: usize, marker: ::std::marker::PhantomData #phantom_data, } impl #impl_generics ::strum::IntoEnumIterator for #name #ty_generics #where_clause { type Iterator = #iter_name #ty_generics; fn iter() -> #iter_name #ty_generics { #iter_name { idx:0, marker: ::std::marker::PhantomData, } } } 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),* }; self.idx += 1; output } fn size_hint(&self) -> (usize, Option) { let t = #variant_count - self.idx; (t, Some(t)) } } impl #impl_generics ExactSizeIterator for #iter_name #ty_generics #where_clause { fn len(&self) -> usize { self.size_hint().0 } } impl #impl_generics Clone for #iter_name #ty_generics #where_clause { fn clone(&self) -> #iter_name #ty_generics { #iter_name { idx: self.idx, marker: self.marker.clone(), } } } } } strum_macros-0.15.0/src/enum_messages.rs000064400000000000000000000063311343715223200164760ustar0000000000000000use proc_macro2::TokenStream; use syn; use helpers::{extract_attrs, extract_meta, is_disabled, unique_attr}; pub fn enum_message_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("EnumMessage only works on Enums"), }; let mut arms = Vec::new(); let mut detailed_arms = Vec::new(); let mut serializations = Vec::new(); for variant in variants { 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::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(&meta, "strum", "serialize"); if serialization_variants.len() == 0 { serialization_variants.push(ident.to_string()); } let count = serialization_variants.len(); serializations.push(quote! { &#name::#ident #params => { static ARR: [&'static str; #count] = [#(#serialization_variants),*]; &ARR } }); } // But you can disable the messages. if is_disabled(&meta) { continue; } if let Some(msg) = messages { let params = params.clone(); // Push the simple message. let tokens = quote! { &#name::#ident #params => ::std::option::Option::Some(#msg) }; arms.push(tokens.clone()); if detailed_messages.is_none() { detailed_arms.push(tokens); } } if let Some(msg) = detailed_messages { let params = params.clone(); // Push the simple message. detailed_arms .push(quote! { &#name::#ident #params => ::std::option::Option::Some(#msg) }); } } if arms.len() < variants.len() { arms.push(quote! { _ => ::std::option::Option::None }); } if detailed_arms.len() < variants.len() { detailed_arms.push(quote! { _ => ::std::option::Option::None }); } quote! { impl #impl_generics ::strum::EnumMessage for #name #ty_generics #where_clause { fn get_message(&self) -> ::std::option::Option<&str> { match self { #(#arms),* } } fn get_detailed_message(&self) -> ::std::option::Option<&str> { match self { #(#detailed_arms),* } } fn get_serializations(&self) -> &[&str] { match self { #(#serializations),* } } } } } strum_macros-0.15.0/src/enum_properties.rs000064400000000000000000000071131343715223200170620ustar0000000000000000use proc_macro2::TokenStream; use syn; use syn::Meta; use helpers::{extract_meta, is_disabled}; 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" { Some(nested) } else { None } } _ => None, }) .flat_map(|prop| prop) .filter_map(|prop| match *prop { NestedMeta::Meta(Meta::List(MetaList { ref ident, ref nested, .. })) => { if ident == "props" { Some(nested) } else { None } } _ => None, }) .flat_map(|prop| prop) // 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() } pub fn enum_properties_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 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(&meta) { continue; } use syn::Fields::*; let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; for (key, value) in extract_properties(&meta) { use syn::Lit::*; let key = key.to_string(); match value { Str(ref s, ..) => { string_arms.push(quote! { #key => ::std::option::Option::Some( #s )}) } Bool(b) => bool_arms.push(quote! { #key => ::std::option::Option::Some( #b )}), Int(i, ..) => num_arms.push(quote! { #key => ::std::option::Option::Some( #i )}), _ => {} } } string_arms.push(quote! { _ => ::std::option::Option::None }); bool_arms.push(quote! { _ => ::std::option::Option::None }); num_arms.push(quote! { _ => ::std::option::Option::None }); arms.push(quote! { &#name::#ident #params => { match prop { #(#string_arms),* } } }); } if arms.len() < variants.len() { arms.push(quote! { _ => ::std::option::Option::None }); } quote! { impl #impl_generics ::strum::EnumProperty for #name #ty_generics #where_clause { fn get_str(&self, prop: &str) -> ::std::option::Option<&'static str> { match self { #(#arms),* } } } } } strum_macros-0.15.0/src/from_string.rs000064400000000000000000000063241343715223200161760ustar0000000000000000use proc_macro2::TokenStream; use syn; use case_style::CaseStyle; use helpers::{convert_case, extract_attrs, extract_meta, is_disabled, unique_attr}; pub fn from_string_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("FromString only works on Enums"), }; let type_meta = extract_meta(&ast.attrs); let case_style = unique_attr(&type_meta, "strum", "serialize_all") .map(|style| CaseStyle::from(style.as_ref())); let mut has_default = false; let mut default = quote! { _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound) }; let mut arms = Vec::new(); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let meta = extract_meta(&variant.attrs); // Look at all the serialize attributes. let mut attrs = extract_attrs(&meta, "strum", "serialize"); attrs.extend(extract_attrs(&meta, "strum", "to_string")); if is_disabled(&meta) { continue; } if unique_attr(&meta, "strum", "default").map_or(false, |s| s == "true") { if has_default { panic!("Can't have multiple default variants"); } if let Unnamed(ref fields) = variant.fields { if fields.unnamed.len() != 1 { panic!("Default only works on unit structs with a single String parameter"); } default = quote! { default => ::std::result::Result::Ok(#name::#ident (default.into())) }; } else { panic!("Default only works on unit structs with a single String parameter"); } has_default = true; continue; } // If we don't have any custom variants, add the default serialized name. if attrs.len() == 0 { attrs.push(convert_case(ident, case_style)); } let params = match variant.fields { Unit => quote! {}, Unnamed(ref fields) => { let defaults = ::std::iter::repeat(quote!(Default::default())).take(fields.unnamed.len()); quote! { (#(#defaults),*) } } Named(ref fields) => { let fields = fields .named .iter() .map(|field| field.ident.as_ref().unwrap()); quote! { {#(#fields: Default::default()),*} } } }; arms.push(quote! { #(#attrs)|* => ::std::result::Result::Ok(#name::#ident #params) }); } arms.push(default); quote! { impl #impl_generics ::std::str::FromStr for #name #ty_generics #where_clause { type Err = ::strum::ParseError; fn from_str(s: &str) -> ::std::result::Result< #name #ty_generics , Self::Err> { match s { #(#arms),* } } } } } strum_macros-0.15.0/src/helpers.rs000064400000000000000000000117551343715223200153130ustar0000000000000000use heck::{CamelCase, KebabCase, MixedCase, ShoutySnakeCase, SnakeCase, TitleCase}; use syn::{Attribute, Ident, Meta, MetaList}; use case_style::CaseStyle; pub fn extract_meta(attrs: &[Attribute]) -> Vec { attrs .iter() .filter_map(|attribute| attribute.interpret_meta()) .collect() } pub fn filter_metas<'meta, MetaIt, F>(metas: MetaIt, filter: F) -> impl Iterator where MetaIt: Iterator, F: Fn(&Meta) -> bool, { metas.filter_map(move |meta| if filter(meta) { Some(meta) } else { None }) } pub fn filter_meta_lists<'meta, MetaIt, F>( metas: MetaIt, filter: F, ) -> impl Iterator where MetaIt: Iterator, F: Fn(&MetaList) -> bool, { metas.filter_map(move |meta| match meta { Meta::List(ref metalist) => { if filter(metalist) { Some(metalist) } else { None } } _ => None, }) } /// Returns the `MetaList`s with the given attr name. /// /// For example, `get_meta_list(type_meta.iter(), "strum_discriminant")` for the following snippet /// will return an iterator with `#[strum_discriminant(derive(EnumIter))]` and /// `#[strum_discriminant(name(MyEnumVariants))]`. /// /// ```rust,ignore /// #[derive(Debug)] /// #[strum_discriminant(derive(EnumIter))] /// #[strum_discriminant(name(MyEnumVariants))] /// enum MyEnum { A } /// ``` pub fn get_meta_list<'meta, MetaIt>( metas: MetaIt, attr: &'meta str, ) -> impl Iterator where MetaIt: Iterator, { filter_meta_lists(metas, move |metalist| metalist.ident == attr) } /// Returns the `MetaList` that matches the given name from the list of `Meta`s, or `None`. /// /// # Panics /// /// Panics if more than one `Meta` exists with the name. pub fn unique_meta_list<'meta, MetaIt>(metas: MetaIt, attr: &'meta str) -> Option<&'meta MetaList> where MetaIt: Iterator, { let mut curr = get_meta_list(metas.into_iter(), attr).collect::>(); if curr.len() > 1 { panic!("More than one `{}` attribute found on type", attr); } curr.pop() } /// Returns an iterator of the `Meta`s from the given `MetaList`. pub fn extract_list_metas<'meta>(metalist: &'meta MetaList) -> impl Iterator { use syn::NestedMeta; metalist.nested.iter().filter_map(|nested| match *nested { NestedMeta::Meta(ref meta) => Some(meta), _ => None, }) } /// Returns the `Ident` of the `Meta::Word`, or `None`. pub fn get_meta_ident<'meta>(meta: &'meta Meta) -> Option<&'meta Ident> { match *meta { Meta::Word(ref ident) => Some(ident), _ => None, } } 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(|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(|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(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); } curr.pop() } 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'"), } } pub fn convert_case(ident: &Ident, case_style: Option) -> String { let ident_string = ident.to_string(); if let Some(case_style) = case_style { match case_style { CaseStyle::CamelCase => ident_string.to_camel_case(), CaseStyle::KebabCase => ident_string.to_kebab_case(), CaseStyle::MixedCase => ident_string.to_mixed_case(), CaseStyle::ShoutySnakeCase => ident_string.to_shouty_snake_case(), CaseStyle::SnakeCase => ident_string.to_snake_case(), CaseStyle::TitleCase => ident_string.to_title_case(), } } else { ident_string } } strum_macros-0.15.0/src/lib.rs000064400000000000000000000136751344131011000144040ustar0000000000000000//! # Strum //! //! Strum is a set of macros and traits for working with //! enums and strings easier in Rust. //! //! # Documentation //! //! The documentation for this crate is found in the `strum` crate. #![recursion_limit = "128"] extern crate heck; #[macro_use] extern crate syn; #[macro_use] extern crate quote; extern crate proc_macro; extern crate proc_macro2; mod as_ref_str; mod case_style; mod display; mod enum_count; mod enum_discriminants; mod enum_iter; mod enum_messages; mod enum_properties; mod from_string; mod helpers; mod to_string; use proc_macro2::TokenStream; use std::env; fn debug_print_generated(ast: &syn::DeriveInput, toks: &TokenStream) { let debug = env::var("STRUM_DEBUG"); if let Ok(s) = debug { if s == "1" { println!("{}", toks); } if ast.ident == s { println!("{}", toks); } } } #[cfg_attr(not(feature = "verbose-enumstring-name"), proc_macro_derive(EnumString, attributes(strum)))] #[cfg_attr(feature = "verbose-enumstring-name", proc_macro_derive(StrumEnumString, attributes(strum)))] pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = from_string::from_string_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(not(feature = "verbose-asrefstr-name"), proc_macro_derive(AsRefStr, attributes(strum)))] #[cfg_attr(feature = "verbose-asrefstr-name", proc_macro_derive(StrumAsRefStr, attributes(strum)))] pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = as_ref_str::as_ref_str_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-asstaticstr-name", proc_macro_derive(StrumAsStaticStr, attributes(strum)))] #[cfg_attr(not(feature = "verbose-asstaticstr-name"), proc_macro_derive(AsStaticStr, attributes(strum)))] pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = as_ref_str::as_static_str_inner(&ast, as_ref_str::GenerateTraitVariant::AsStaticStr); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-intostaticstr-name", proc_macro_derive(StrumIntoStaticStr, attributes(strum)))] #[cfg_attr(not(feature = "verbose-intostaticstr-name"), proc_macro_derive(IntoStaticStr, attributes(strum)))] pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = as_ref_str::as_static_str_inner(&ast, as_ref_str::GenerateTraitVariant::From); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-tostring-name", proc_macro_derive(StrumToString, attributes(strum)))] #[cfg_attr(not(feature = "verbose-tostring-name"), proc_macro_derive(ToString, attributes(strum)))] pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = to_string::to_string_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-display-name", proc_macro_derive(StrumDisplay, attributes(strum)))] #[cfg_attr(not(feature = "verbose-display-name"), proc_macro_derive(Display, attributes(strum)))] pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = display::display_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-enumiter-name", proc_macro_derive(StrumEnumIter, attributes(strum)))] #[cfg_attr(not(feature = "verbose-enumiter-name"), proc_macro_derive(EnumIter, attributes(strum)))] pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = enum_iter::enum_iter_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-enummessage-name", proc_macro_derive(StrumEnumMessage, attributes(strum)))] #[cfg_attr(not(feature = "verbose-enummessage-name"), proc_macro_derive(EnumMessage, attributes(strum)))] pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = enum_messages::enum_message_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-enumproperty-name", proc_macro_derive(StrumEnumProperty, attributes(strum)))] #[cfg_attr(not(feature = "verbose-enumproperty-name"), proc_macro_derive(EnumProperty, attributes(strum)))] pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = enum_properties::enum_properties_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-enumdiscriminants-name", proc_macro_derive(StrumEnumDiscriminants, attributes(strum, strum_discriminants)))] #[cfg_attr(not(feature = "verbose-enumdiscriminants-name"), proc_macro_derive(EnumDiscriminants, attributes(strum, strum_discriminants)))] pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = enum_discriminants::enum_discriminants_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } #[cfg_attr(feature = "verbose-enumcount-name", proc_macro_derive(StrumEnumCount, attributes(strum)))] #[cfg_attr(not(feature = "verbose-enumcount-name"), proc_macro_derive(EnumCount, attributes(strum)))] pub fn enum_count(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); let toks = enum_count::enum_count_inner(&ast); debug_print_generated(&ast, &toks); toks.into() } strum_macros-0.15.0/src/to_string.rs000064400000000000000000000041501343715223200156500ustar0000000000000000use proc_macro2::TokenStream; use syn; use case_style::CaseStyle; use helpers::{convert_case, extract_attrs, extract_meta, is_disabled, unique_attr}; pub fn to_string_inner(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let variants = match ast.data { syn::Data::Enum(ref v) => &v.variants, _ => panic!("ToString only works on Enums"), }; let type_meta = extract_meta(&ast.attrs); let case_style = unique_attr(&type_meta, "strum", "serialize_all") .map(|style| CaseStyle::from(style.as_ref())); let mut arms = Vec::new(); for variant in variants { use syn::Fields::*; let ident = &variant.ident; let meta = extract_meta(&variant.attrs); if is_disabled(&meta) { continue; } // Look at all the serialize attributes. let output = if let Some(n) = unique_attr(&meta, "strum", "to_string") { n } else { 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()); if let Some(n) = attrs.pop() { n } else { convert_case(ident, case_style) } }; let params = match variant.fields { Unit => quote! {}, Unnamed(..) => quote! { (..) }, Named(..) => quote! { {..} }, }; arms.push(quote! { #name::#ident #params => ::std::string::String::from(#output) }); } if arms.len() < variants.len() { arms.push(quote! { _ => panic!("to_string() called on disabled variant.")}) } quote! { impl #impl_generics ::std::string::ToString for #name #ty_generics #where_clause { fn to_string(&self) -> ::std::string::String { match *self { #(#arms),* } } } } }