diff --git a/enum-kinds/README.md b/enum-kinds/README.md index 587ec40..349f315 100644 --- a/enum-kinds/README.md +++ b/enum-kinds/README.md @@ -65,6 +65,26 @@ enum AdditionalDerives { } ``` +# Custom Discriminant Values + +By default, derived kind enums do not specify values for the variants. To +specify values, use the `enum_kind_value` attribute: `#[enum_kind_value(10)]`. +For example, + +``` rust,ignore +#[macro_use] +extern crate enum_kinds; + +#[derive(EnumKind)] +#[enum_kind(WithValuesKind)] +enum WithValues { + #[enum_kind_value(10)] + Ten(String, u32), + #[enum_kind_value(20)] + Twenty(String) +} +``` + # no_std support `enum-kinds` can be used without the standard library by enabling `no-stdlib` diff --git a/enum-kinds/src/lib.rs b/enum-kinds/src/lib.rs index 97422a1..0378881 100755 --- a/enum-kinds/src/lib.rs +++ b/enum-kinds/src/lib.rs @@ -11,11 +11,11 @@ use quote::quote; use std::collections::HashSet; use syn::punctuated::Punctuated; use syn::{ - Data, DataEnum, DeriveInput, Fields, GenericParam, Lifetime, LifetimeDef, Meta, MetaList, - MetaNameValue, NestedMeta, Path, + Attribute, Data, DataEnum, DeriveInput, Fields, GenericParam, Lifetime, LifetimeDef, Meta, + MetaList, MetaNameValue, NestedMeta, Path, }; -#[proc_macro_derive(EnumKind, attributes(enum_kind))] +#[proc_macro_derive(EnumKind, attributes(enum_kind, enum_kind_value))] pub fn enum_kind(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).expect("#[derive(EnumKind)] failed to parse input"); let (name, traits) = get_enum_specification(&ast); @@ -29,10 +29,10 @@ pub fn enum_kind(input: proc_macro::TokenStream) -> proc_macro::TokenStream { } fn find_attribute( - definition: &DeriveInput, + attrs: &[Attribute], name: &str, ) -> Option> { - for attr in definition.attrs.iter() { + for attr in attrs.iter() { match attr.parse_meta() { Ok(Meta::List(MetaList { ref path, @@ -46,7 +46,7 @@ fn find_attribute( } fn get_enum_specification(definition: &DeriveInput) -> (Path, Vec) { - let params = find_attribute(definition, "enum_kind") + let params = find_attribute(&definition.attrs, "enum_kind") .expect("#[derive(EnumKind)] requires an associated enum_kind attribute to be specified"); let mut iter = params.iter(); if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = iter.next() { @@ -71,12 +71,19 @@ fn create_kind_enum( kind_ident: &Path, traits: Vec, ) -> TokenStream { - let variant_idents = match &definition.data { - &Data::Enum(DataEnum { ref variants, .. }) => variants.iter().map(|ref v| v.ident.clone()), + let variants = match &definition.data { + &Data::Enum(DataEnum { ref variants, .. }) => variants, _ => { panic!("#[derive(EnumKind)] is only allowed for enums"); } }; + let variant_defs = variants.iter().map(|ref v| { + let ident = v.ident.clone(); + match find_attribute(&v.attrs, "enum_kind_value") { + Some(params) => quote! {#ident = #params}, + None => quote! {#ident}, + } + }); let visibility = &definition.vis; let docs_attr = if !has_docs(traits.as_ref()) { quote! {#[allow(missing_docs)]} @@ -89,7 +96,7 @@ fn create_kind_enum( #docs_attr #( #[#traits] )* #visibility enum #kind_ident { - #(#variant_idents),* + #(#variant_defs),* } }; TokenStream::from(code) diff --git a/enum-kinds/tests/kinds.rs b/enum-kinds/tests/kinds.rs index 2dc5114..90d0e83 100644 --- a/enum-kinds/tests/kinds.rs +++ b/enum-kinds/tests/kinds.rs @@ -72,6 +72,17 @@ enum WithExtraTraitsMultiple { Second(String), } +#[derive(EnumKind)] +#[enum_kind(WithValuesKind)] +#[allow(dead_code)] +enum WithValues { + #[enum_kind_value(10)] + Ten, + #[enum_kind_value(20)] + Twenty, + TwentyOne, +} + mod forbids_missing_docs { #![forbid(missing_docs)] @@ -133,3 +144,10 @@ fn test_with_extra_traits_multiple() { let kind: WithExtraTraitsMultipleKind = first.into(); serde_json::to_string(&kind).unwrap(); } + +#[test] +fn test_with_values() { + let twentyone = WithValues::TwentyOne; + let kind: WithValuesKind = twentyone.into(); + assert_eq!(kind as usize, 21); +}