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
16 changes: 3 additions & 13 deletions strum_macros/src/helpers/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use proc_macro2::TokenStream;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
Expand Down Expand Up @@ -126,8 +125,7 @@ pub enum EnumDiscriminantsMeta {
Derive { _kw: kw::derive, paths: Vec<Path> },
Name { kw: kw::name, name: Ident },
Vis { kw: kw::vis, vis: Visibility },
Doc { _kw: kw::doc, doc: LitStr },
Other { path: Path, nested: TokenStream },
Other { passthrough_meta: Meta },
}

impl Parse for EnumDiscriminantsMeta {
Expand All @@ -153,17 +151,9 @@ impl Parse for EnumDiscriminantsMeta {
parenthesized!(content in input);
let vis = content.parse()?;
Ok(EnumDiscriminantsMeta::Vis { kw, vis })
} else if input.peek(kw::doc) {
let _kw = input.parse()?;
input.parse::<Token![=]>()?;
let doc = input.parse()?;
Ok(EnumDiscriminantsMeta::Doc { _kw, doc })
} else {
let path = input.parse()?;
let content;
parenthesized!(content in input);
let nested = content.parse()?;
Ok(EnumDiscriminantsMeta::Other { path, nested })
let passthrough_meta = input.parse()?;
Ok(EnumDiscriminantsMeta::Other { passthrough_meta })
}
}
}
Expand Down
13 changes: 4 additions & 9 deletions strum_macros/src/helpers/type_props.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use proc_macro2::TokenStream;
use quote::quote;
use std::default::Default;
use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility};
use syn::{parse_quote, DeriveInput, Ident, LitStr, Meta, Path, Visibility};

use super::case_style::CaseStyle;
use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta};
Expand All @@ -20,14 +19,13 @@ pub struct StrumTypeProperties {
pub crate_module_path: Option<Path>,
pub discriminant_derives: Vec<Path>,
pub discriminant_name: Option<Ident>,
pub discriminant_others: Vec<TokenStream>,
pub discriminant_others: Vec<Meta>,
pub discriminant_vis: Option<Visibility>,
pub use_phf: bool,
pub prefix: Option<LitStr>,
pub suffix: Option<LitStr>,
pub enum_repr: Option<TokenStream>,
pub const_into_str: bool,
pub discriminant_docs: Vec<LitStr>,
}

impl HasTypeProperties for DeriveInput {
Expand Down Expand Up @@ -150,11 +148,8 @@ impl HasTypeProperties for DeriveInput {
vis_kw = Some(kw);
output.discriminant_vis = Some(vis);
}
EnumDiscriminantsMeta::Doc { doc, .. } => {
output.discriminant_docs.push(doc);
}
EnumDiscriminantsMeta::Other { path, nested } => {
output.discriminant_others.push(quote! { #path(#nested) });
EnumDiscriminantsMeta::Other { passthrough_meta } => {
output.discriminant_others.push(passthrough_meta);
}
}
}
Expand Down
26 changes: 15 additions & 11 deletions strum_macros/src/macros/enum_discriminants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, #(#derives),*)]
};

// Create #[doc] attrs for new generated type.
let docs = type_properties.discriminant_docs;

let docs = quote! {
#(#[doc = #docs])*
};

// Work out the name
let default_name = syn::Ident::new(&format!("{}Discriminants", name), Span::call_site());

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

// Pass through all other attributes
let pass_though_attributes = type_properties.discriminant_others;
// Pass through all other attributes and add doc if there is none
let pass_through_attributes = type_properties.discriminant_others;
let has_doc = pass_through_attributes
.iter()
.any(|meta| meta.path().is_ident("doc"));
let mut pass_through_attributes: Vec<_> = pass_through_attributes
.into_iter()
.map(ToTokens::into_token_stream)
.collect();
if !has_doc {
pass_through_attributes.push(quote! {
doc = "Auto-generated discriminant enum variants"
});
}

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

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

Ok(quote! {
#docs
#derives
#repr
#(#[ #pass_though_attributes ])*
#(#[ #pass_through_attributes ])*
#discriminants_vis enum #discriminants_name {
#(#discriminants),*
}
Expand Down
37 changes: 37 additions & 0 deletions strum_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,40 @@ pub enum Color {
#[strum(disabled)]
Green(String),
}

/// A bunch of errors
///
/// This will not work:
///
/// ```compile_fail
/// # use strum_tests::{Errors, ErrorsDiscriminants};
/// fn expect_path_error(error: &Errors) {
/// let discriminant: ErrorsDiscriminants = error.into();
/// match discriminant {
/// ErrorsDiscriminants::PathError => (),
/// ErrorsDiscriminants::NotFound => panic!("should be a path error"),
/// }
/// }
/// ```
///
/// This will work:
///
/// ```
/// # use strum_tests::{Errors, ErrorsDiscriminants};
/// fn expect_path_error(error: &Errors) {
/// let discriminant: ErrorsDiscriminants = error.into();
/// match discriminant {
/// ErrorsDiscriminants::PathError => (),
/// ErrorsDiscriminants::NotFound => panic!("should be a path error"),
/// _ => panic!("unknown error, should be a path error"),
/// }
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants)]
#[non_exhaustive]
#[strum_discriminants(doc = "Discriminants-only version of a bunch of errors")]
#[strum_discriminants(non_exhaustive)]
pub enum Errors {
NotFound,
PathError(String),
}
12 changes: 12 additions & 0 deletions strum_tests/tests/enum_discriminants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use strum::{
Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, FromRepr, IntoEnumIterator,
VariantArray,
};
use strum_tests::{Errors, ErrorsDiscriminants};

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

Expand Down Expand Up @@ -341,6 +342,17 @@ fn with_explicit_discriminant_value() {
);
}

#[test]
fn non_exhaustive_enum() {
let error = Errors::PathError("some_path".into());
let error_discriminant: ErrorsDiscriminants = error.into();
match error_discriminant {
ErrorsDiscriminants::NotFound => unreachable!(),
ErrorsDiscriminants::PathError => (),
_ => unreachable!(),
}
}

#[allow(dead_code)]
#[derive(Debug, Eq, PartialEq, EnumIter, EnumDiscriminants)]
#[strum_discriminants(derive(EnumIter, VariantArray))]
Expand Down