Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions enum-kinds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
25 changes: 16 additions & 9 deletions enum-kinds/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<Punctuated<NestedMeta, syn::token::Comma>> {
for attr in definition.attrs.iter() {
for attr in attrs.iter() {
match attr.parse_meta() {
Ok(Meta::List(MetaList {
ref path,
Expand All @@ -46,7 +46,7 @@ fn find_attribute(
}

fn get_enum_specification(definition: &DeriveInput) -> (Path, Vec<NestedMeta>) {
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() {
Expand All @@ -71,12 +71,19 @@ fn create_kind_enum(
kind_ident: &Path,
traits: Vec<NestedMeta>,
) -> 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)]}
Expand All @@ -89,7 +96,7 @@ fn create_kind_enum(
#docs_attr
#( #[#traits] )*
#visibility enum #kind_ident {
#(#variant_idents),*
#(#variant_defs),*
}
};
TokenStream::from(code)
Expand Down
18 changes: 18 additions & 0 deletions enum-kinds/tests/kinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]

Expand Down Expand Up @@ -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);
}