Skip to content

Commit 639b67f

Browse files
authored
feat: allow any kind of passthrough attributes on EnumDiscriminants (#461)
Port of clechasseur#15
1 parent 0ea1e2d commit 639b67f

5 files changed

Lines changed: 71 additions & 33 deletions

File tree

strum_macros/src/helpers/metadata.rs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use proc_macro2::TokenStream;
21
use syn::{
32
parenthesized,
43
parse::{Parse, ParseStream},
@@ -126,8 +125,7 @@ pub enum EnumDiscriminantsMeta {
126125
Derive { _kw: kw::derive, paths: Vec<Path> },
127126
Name { kw: kw::name, name: Ident },
128127
Vis { kw: kw::vis, vis: Visibility },
129-
Doc { _kw: kw::doc, doc: LitStr },
130-
Other { path: Path, nested: TokenStream },
128+
Other { passthrough_meta: Meta },
131129
}
132130

133131
impl Parse for EnumDiscriminantsMeta {
@@ -153,17 +151,9 @@ impl Parse for EnumDiscriminantsMeta {
153151
parenthesized!(content in input);
154152
let vis = content.parse()?;
155153
Ok(EnumDiscriminantsMeta::Vis { kw, vis })
156-
} else if input.peek(kw::doc) {
157-
let _kw = input.parse()?;
158-
input.parse::<Token![=]>()?;
159-
let doc = input.parse()?;
160-
Ok(EnumDiscriminantsMeta::Doc { _kw, doc })
161154
} else {
162-
let path = input.parse()?;
163-
let content;
164-
parenthesized!(content in input);
165-
let nested = content.parse()?;
166-
Ok(EnumDiscriminantsMeta::Other { path, nested })
155+
let passthrough_meta = input.parse()?;
156+
Ok(EnumDiscriminantsMeta::Other { passthrough_meta })
167157
}
168158
}
169159
}

strum_macros/src/helpers/type_props.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use proc_macro2::TokenStream;
2-
use quote::quote;
32
use std::default::Default;
4-
use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility};
3+
use syn::{parse_quote, DeriveInput, Ident, LitStr, Meta, Path, Visibility};
54

65
use super::case_style::CaseStyle;
76
use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta};
@@ -20,14 +19,13 @@ pub struct StrumTypeProperties {
2019
pub crate_module_path: Option<Path>,
2120
pub discriminant_derives: Vec<Path>,
2221
pub discriminant_name: Option<Ident>,
23-
pub discriminant_others: Vec<TokenStream>,
22+
pub discriminant_others: Vec<Meta>,
2423
pub discriminant_vis: Option<Visibility>,
2524
pub use_phf: bool,
2625
pub prefix: Option<LitStr>,
2726
pub suffix: Option<LitStr>,
2827
pub enum_repr: Option<TokenStream>,
2928
pub const_into_str: bool,
30-
pub discriminant_docs: Vec<LitStr>,
3129
}
3230

3331
impl HasTypeProperties for DeriveInput {
@@ -150,11 +148,8 @@ impl HasTypeProperties for DeriveInput {
150148
vis_kw = Some(kw);
151149
output.discriminant_vis = Some(vis);
152150
}
153-
EnumDiscriminantsMeta::Doc { doc, .. } => {
154-
output.discriminant_docs.push(doc);
155-
}
156-
EnumDiscriminantsMeta::Other { path, nested } => {
157-
output.discriminant_others.push(quote! { #path(#nested) });
151+
EnumDiscriminantsMeta::Other { passthrough_meta } => {
152+
output.discriminant_others.push(passthrough_meta);
158153
}
159154
}
160155
}

strum_macros/src/macros/enum_discriminants.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
3030
#[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)]
3131
};
3232

33-
// Create #[doc] attrs for new generated type.
34-
let docs = type_properties.discriminant_docs;
35-
36-
let docs = quote! {
37-
#(#[doc = #docs])*
38-
};
39-
4033
// Work out the name
4134
let default_name = syn::Ident::new(&format!("{}Discriminants", name), Span::call_site());
4235

@@ -46,8 +39,20 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
4639
.as_ref()
4740
.unwrap_or_else(|| &vis);
4841

49-
// Pass through all other attributes
50-
let pass_though_attributes = type_properties.discriminant_others;
42+
// Pass through all other attributes and add doc if there is none
43+
let pass_through_attributes = type_properties.discriminant_others;
44+
let has_doc = pass_through_attributes
45+
.iter()
46+
.any(|meta| meta.path().is_ident("doc"));
47+
let mut pass_through_attributes: Vec<_> = pass_through_attributes
48+
.into_iter()
49+
.map(ToTokens::into_token_stream)
50+
.collect();
51+
if !has_doc {
52+
pass_through_attributes.push(quote! {
53+
doc = "Auto-generated discriminant enum variants"
54+
});
55+
}
5156

5257
let repr = type_properties.enum_repr.map(|repr| quote!(#[repr(#repr)]));
5358

@@ -196,10 +201,9 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
196201
};
197202

198203
Ok(quote! {
199-
#docs
200204
#derives
201205
#repr
202-
#(#[ #pass_though_attributes ])*
206+
#(#[ #pass_through_attributes ])*
203207
#discriminants_vis enum #discriminants_name {
204208
#(#discriminants),*
205209
}

strum_tests/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,40 @@ pub enum Color {
1313
#[strum(disabled)]
1414
Green(String),
1515
}
16+
17+
/// A bunch of errors
18+
///
19+
/// This will not work:
20+
///
21+
/// ```compile_fail
22+
/// # use strum_tests::{Errors, ErrorsDiscriminants};
23+
/// fn expect_path_error(error: &Errors) {
24+
/// let discriminant: ErrorsDiscriminants = error.into();
25+
/// match discriminant {
26+
/// ErrorsDiscriminants::PathError => (),
27+
/// ErrorsDiscriminants::NotFound => panic!("should be a path error"),
28+
/// }
29+
/// }
30+
/// ```
31+
///
32+
/// This will work:
33+
///
34+
/// ```
35+
/// # use strum_tests::{Errors, ErrorsDiscriminants};
36+
/// fn expect_path_error(error: &Errors) {
37+
/// let discriminant: ErrorsDiscriminants = error.into();
38+
/// match discriminant {
39+
/// ErrorsDiscriminants::PathError => (),
40+
/// ErrorsDiscriminants::NotFound => panic!("should be a path error"),
41+
/// _ => panic!("unknown error, should be a path error"),
42+
/// }
43+
/// }
44+
/// ```
45+
#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants)]
46+
#[non_exhaustive]
47+
#[strum_discriminants(doc = "Discriminants-only version of a bunch of errors")]
48+
#[strum_discriminants(non_exhaustive)]
49+
pub enum Errors {
50+
NotFound,
51+
PathError(String),
52+
}

strum_tests/tests/enum_discriminants.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use strum::{
55
Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, FromRepr, IntoEnumIterator,
66
VariantArray,
77
};
8+
use strum_tests::{Errors, ErrorsDiscriminants};
89

910
mod core {} // ensure macros call `::core`
1011

@@ -341,6 +342,17 @@ fn with_explicit_discriminant_value() {
341342
);
342343
}
343344

345+
#[test]
346+
fn non_exhaustive_enum() {
347+
let error = Errors::PathError("some_path".into());
348+
let error_discriminant: ErrorsDiscriminants = error.into();
349+
match error_discriminant {
350+
ErrorsDiscriminants::NotFound => unreachable!(),
351+
ErrorsDiscriminants::PathError => (),
352+
_ => unreachable!(),
353+
}
354+
}
355+
344356
#[allow(dead_code)]
345357
#[derive(Debug, Eq, PartialEq, EnumIter, EnumDiscriminants)]
346358
#[strum_discriminants(derive(EnumIter, VariantArray))]

0 commit comments

Comments
 (0)