Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attributes to strum_discriminants for custom attributes in generated discriminant enum #320

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
7 changes: 5 additions & 2 deletions strum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ impl std::error::Error for ParseError {
/// generic_iterator::<Color, _>(|color| println!("{:?}", color));
/// ```
pub trait IntoEnumIterator: Sized {
type Iterator: Iterator<Item = Self> + Clone + DoubleEndedIterator + ExactSizeIterator + FusedIterator;

type Iterator: Iterator<Item = Self>
+ Clone
+ DoubleEndedIterator
+ ExactSizeIterator
+ FusedIterator;
fn iter() -> Self::Iterator;
}

Expand Down
3 changes: 2 additions & 1 deletion strum_macros/src/helpers/case_style.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use heck::{
ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToUpperCamelCase, ToTrainCase,
ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, ToTrainCase,
ToUpperCamelCase,
};
use std::str::FromStr;
use syn::{
Expand Down
38 changes: 32 additions & 6 deletions strum_macros/src/helpers/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use syn::{
parse::{Parse, ParseStream},
parse2, parse_str,
punctuated::Punctuated,
Attribute, DeriveInput, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Meta, MetaNameValue, Path,
Token, Variant, Visibility,
Attribute, DeriveInput, Expr, ExprLit, Ident, Lit, LitBool, LitStr, Meta, MetaList,
MetaNameValue, Path, Token, Variant, Visibility,
};

use super::case_style::CaseStyle;
Expand All @@ -22,6 +22,7 @@ pub mod kw {
custom_keyword!(derive);
custom_keyword!(name);
custom_keyword!(vis);
custom_keyword!(attributes);

// variant metadata
custom_keyword!(message);
Expand Down Expand Up @@ -76,10 +77,26 @@ impl Parse for EnumMeta {
}

pub enum EnumDiscriminantsMeta {
Derive { kw: kw::derive, paths: Vec<Path> },
Name { kw: kw::name, name: Ident },
Vis { kw: kw::vis, vis: Visibility },
Other { path: Path, nested: TokenStream },
Derive {
kw: kw::derive,
paths: Vec<Path>,
},
Name {
kw: kw::name,
name: Ident,
},
Vis {
kw: kw::vis,
vis: Visibility,
},
Attributes {
kw: kw::attributes,
attributes: Vec<MetaList>,
},
Other {
path: Path,
nested: TokenStream,
},
}

impl Parse for EnumDiscriminantsMeta {
Expand All @@ -105,6 +122,15 @@ impl Parse for EnumDiscriminantsMeta {
parenthesized!(content in input);
let vis = content.parse()?;
Ok(EnumDiscriminantsMeta::Vis { kw, vis })
} else if input.peek(kw::attributes) {
let kw = input.parse()?;
let content;
parenthesized!(content in input);
let attributes = content.parse_terminated(MetaList::parse, Token![,])?;
Ok(EnumDiscriminantsMeta::Attributes {
kw,
attributes: attributes.into_iter().collect(),
})
} else {
let path = input.parse()?;
let content;
Expand Down
2 changes: 1 addition & 1 deletion strum_macros/src/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub use self::case_style::{CaseStyleHelpers, snakify};
pub use self::case_style::{snakify};
pub use self::type_props::HasTypeProperties;
pub use self::variant_props::HasStrumVariantProperties;

Expand Down
6 changes: 5 additions & 1 deletion strum_macros/src/helpers/type_props.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use quote::quote;
use std::default::Default;
use syn::{parse_quote, DeriveInput, Ident, Path, Visibility};
use syn::{parse_quote, DeriveInput, Ident, MetaList, Path, Visibility};

use super::case_style::CaseStyle;
use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta};
Expand All @@ -16,6 +16,7 @@ pub struct StrumTypeProperties {
pub case_style: Option<CaseStyle>,
pub ascii_case_insensitive: bool,
pub crate_module_path: Option<Path>,
pub discriminant_attributes: Vec<MetaList>,
pub discriminant_derives: Vec<Path>,
pub discriminant_name: Option<Ident>,
pub discriminant_others: Vec<TokenStream>,
Expand Down Expand Up @@ -98,6 +99,9 @@ impl HasTypeProperties for DeriveInput {
vis_kw = Some(kw);
output.discriminant_vis = Some(vis);
}
EnumDiscriminantsMeta::Attributes { attributes, .. } => {
output.discriminant_attributes.extend(attributes);
}
EnumDiscriminantsMeta::Other { path, nested } => {
output.discriminant_others.push(quote! { #path(#nested) });
}
Expand Down
19 changes: 18 additions & 1 deletion strum_macros/src/macros/enum_discriminants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::{Span, TokenStream, TokenTree};
use quote::{quote, ToTokens};
use syn::parse_quote;
use syn::{parse_quote, MetaList};
use syn::{Data, DeriveInput, Fields};

use crate::helpers::{non_enum_error, strum_discriminants_passthrough_error, HasTypeProperties};
Expand All @@ -20,6 +20,22 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
_ => return Err(non_enum_error()),
};

// Attributes for the generated enum
let type_properties = ast.get_type_properties()?;

let attributes: Vec<MetaList> = type_properties.discriminant_attributes;

let attributes: Vec<_> = attributes
.iter()
.map(|a| {
quote! {
#[#a]
}
})
.collect();

let attributes = quote! {#(#attributes)*};

// Derives for the generated enum
let type_properties = ast.get_type_properties()?;

Expand Down Expand Up @@ -158,6 +174,7 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {

Ok(quote! {
/// Auto-generated discriminant enum variants
#attributes
#derives
#repr
#(#[ #pass_though_attributes ])*
Expand Down
5 changes: 4 additions & 1 deletion strum_macros/src/macros/enum_is.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {

let variant_name = &variant.ident;
let fn_name = format_ident!("is_{}", snakify(&variant_name.to_string()));
let doc_comment = format!("Returns [true] if the enum is [{}::{}] otherwise [false]", enum_name, variant_name);
let doc_comment = format!(
"Returns [true] if the enum is [{}::{}] otherwise [false]",
enum_name, variant_name
);
Some(quote! {
#[must_use]
#[inline]
Expand Down
19 changes: 11 additions & 8 deletions strum_macros/src/macros/enum_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ pub fn enum_message_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
if !documentation.is_empty() {
let params = params.clone();
// Strip a single leading space from each documentation line.
let documentation: Vec<LitStr> = documentation.iter().map(|lit_str| {
let line = lit_str.value();
if line.starts_with(' ') {
LitStr::new(&line.as_str()[1..], lit_str.span())
} else {
lit_str.clone()
}
}).collect();
let documentation: Vec<LitStr> = documentation
.iter()
.map(|lit_str| {
let line = lit_str.value();
if line.starts_with(' ') {
LitStr::new(&line.as_str()[1..], lit_str.span())
} else {
lit_str.clone()
}
})
.collect();
if documentation.len() == 1 {
let text = &documentation[0];
documentation_arms
Expand Down
2 changes: 1 addition & 1 deletion strum_macros/src/macros/strings/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
}
}
} else {
arms.push(quote! { #name::#ident #params => (#output).fmt(f) } );
arms.push(quote! { #name::#ident #params => (#output).fmt(f) });
}
}

Expand Down
2 changes: 1 addition & 1 deletion strum_macros/src/macros/strings/from_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub fn from_string_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
phf_exact_match_arms.push(quote! { #serialization => #name::#ident #params, });

if is_ascii_case_insensitive {
// Store the lowercase and UPPERCASE variants in the phf map to capture
// Store the lowercase and UPPERCASE variants in the phf map to capture
let ser_string = serialization.value();

let lower =
Expand Down
2 changes: 2 additions & 0 deletions strum_tests/tests/enum_discriminants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct NonDefault;

#[allow(dead_code)]
#[derive(Debug, EnumDiscriminants)]
#[strum_discriminants(attributes(allow(non_camel_case_types), doc()))]
#[strum_discriminants(derive(EnumIter))]
enum WithFields {
Variant0(NonDefault),
Expand Down Expand Up @@ -290,6 +291,7 @@ fn override_visibility() {
fn crate_module_path_test() {
pub mod nested {
pub mod module {
#[allow(unused_imports)]
pub use strum;
}
}
Expand Down
15 changes: 10 additions & 5 deletions strum_tests/tests/enum_is.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ use strum::EnumIs;

mod core {} // ensure macros call `::core`
#[derive(EnumIs)]
enum LifeTimeTest<'a>{
enum LifeTimeTest<'a> {
One(Cow<'a, str>),
Two(&'a str)
Two(&'a str),
}
#[derive(EnumIs)]
enum Foo {
Unit,
Named0 {},
Named1 { _a: char },
Named2 { _a: u32, _b: String },
Named1 {
_a: char,
},
Named2 {
_a: u32,
_b: String,
},
Unnamed0(),
Unnamed1(Option<u128>),
Unnamed2(bool, u8),
Expand All @@ -22,7 +27,7 @@ enum Foo {
Disabled,
}
#[test]
fn generics_test(){
fn generics_test() {
let foo = LifeTimeTest::One(Cow::Borrowed("Hello"));
assert!(foo.is_one());
let foo = LifeTimeTest::Two("Hello");
Expand Down
5 changes: 4 additions & 1 deletion strum_tests/tests/enum_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ fn only_detailed_message() {

#[test]
fn documentation() {
assert_eq!("I eat birds.\n\nAnd fish.\n", (Pets::Cat).get_documentation().unwrap());
assert_eq!(
"I eat birds.\n\nAnd fish.\n",
(Pets::Cat).get_documentation().unwrap()
);
assert_eq!("I'm a fish.", (Pets::Fish).get_documentation().unwrap());
assert_eq!("I'm a bird.", (Pets::Bird).get_documentation().unwrap());
}
Expand Down
5 changes: 4 additions & 1 deletion strum_tests/tests/enum_try_as.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ enum Foo {
#[allow(dead_code)]
Unit,
#[allow(dead_code)]
Named { _a: u32, _b: String },
Named {
_a: u32,
_b: String,
},
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions strum_tests/tests/from_repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ fn const_test() {
fn crate_module_path_test() {
pub mod nested {
pub mod module {
#[allow(unused_imports)]
pub use strum;
}
}
Expand Down