From e9d3b59a80adbd8e7ebf88bf5b63badbcb595799 Mon Sep 17 00:00:00 2001 From: Billy Chan <30400950+billy1624@users.noreply.github.com> Date: Sat, 17 Jul 2021 11:40:19 +0800 Subject: [PATCH] Re-export macros (#170) * Re-exportable macros * Rename derive attribute to `Crate` * Add unit tests * Test nested module * Rename derive attribute to `crate` * cargo fmt --- strum_macros/src/helpers/metadata.rs | 17 ++++++++++ strum_macros/src/helpers/type_props.rs | 25 +++++++++++++- strum_macros/src/macros/enum_count.rs | 6 ++-- strum_macros/src/macros/enum_iter.rs | 6 ++-- strum_macros/src/macros/enum_messages.rs | 3 +- strum_macros/src/macros/enum_properties.rs | 6 ++-- strum_macros/src/macros/enum_variant_names.rs | 3 +- strum_macros/src/macros/strings/as_ref_str.rs | 4 ++- .../src/macros/strings/from_string.rs | 5 +-- strum_tests/tests/enum_count.rs | 24 +++++++++++++ strum_tests/tests/enum_discriminants.rs | 23 +++++++++++++ strum_tests/tests/enum_iter.rs | 34 +++++++++++++++++++ strum_tests/tests/enum_message.rs | 28 +++++++++++++++ strum_tests/tests/enum_props.rs | 21 ++++++++++++ strum_tests/tests/enum_variant_names.rs | 22 ++++++++++++ 15 files changed, 215 insertions(+), 12 deletions(-) diff --git a/strum_macros/src/helpers/metadata.rs b/strum_macros/src/helpers/metadata.rs index bee07d20..6279e6a5 100644 --- a/strum_macros/src/helpers/metadata.rs +++ b/strum_macros/src/helpers/metadata.rs @@ -2,6 +2,7 @@ use proc_macro2::{Span, TokenStream}; use syn::{ parenthesized, parse::{Parse, ParseStream}, + parse2, parse_str, punctuated::Punctuated, spanned::Spanned, Attribute, DeriveInput, Ident, LitBool, LitStr, Path, Token, Variant, Visibility, @@ -11,6 +12,7 @@ use super::case_style::CaseStyle; pub mod kw { use syn::custom_keyword; + pub use syn::token::Crate; // enum metadata custom_keyword!(serialize_all); @@ -37,6 +39,10 @@ pub enum EnumMeta { case_style: CaseStyle, }, AsciiCaseInsensitive(kw::ascii_case_insensitive), + Crate { + kw: kw::Crate, + crate_module_path: Path, + }, } impl Parse for EnumMeta { @@ -47,6 +53,16 @@ impl Parse for EnumMeta { input.parse::()?; let case_style = input.parse()?; Ok(EnumMeta::SerializeAll { kw, case_style }) + } else if lookahead.peek(kw::Crate) { + let kw = input.parse::()?; + input.parse::()?; + let path_str: LitStr = input.parse()?; + let path_tokens = parse_str(&path_str.value())?; + let crate_module_path = parse2(path_tokens)?; + Ok(EnumMeta::Crate { + kw, + crate_module_path, + }) } else if lookahead.peek(kw::ascii_case_insensitive) { let kw = input.parse()?; Ok(EnumMeta::AsciiCaseInsensitive(kw)) @@ -61,6 +77,7 @@ impl Spanned for EnumMeta { match self { EnumMeta::SerializeAll { kw, .. } => kw.span(), EnumMeta::AsciiCaseInsensitive(kw) => kw.span(), + EnumMeta::Crate { kw, .. } => kw.span(), } } } diff --git a/strum_macros/src/helpers/type_props.rs b/strum_macros/src/helpers/type_props.rs index 1e0a42c8..39b0a2bd 100644 --- a/strum_macros/src/helpers/type_props.rs +++ b/strum_macros/src/helpers/type_props.rs @@ -1,7 +1,7 @@ use proc_macro2::TokenStream; use quote::quote; use std::default::Default; -use syn::{DeriveInput, Ident, Path, Visibility}; +use syn::{parse_quote, DeriveInput, Ident, Path, Visibility}; use super::case_style::CaseStyle; use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta}; @@ -15,6 +15,7 @@ pub trait HasTypeProperties { pub struct StrumTypeProperties { pub case_style: Option, pub ascii_case_insensitive: bool, + pub crate_module_path: Option, pub discriminant_derives: Vec, pub discriminant_name: Option, pub discriminant_others: Vec, @@ -30,6 +31,7 @@ impl HasTypeProperties for DeriveInput { let mut serialize_all_kw = None; let mut ascii_case_insensitive_kw = None; + let mut crate_module_path_kw = None; for meta in strum_meta { match meta { EnumMeta::SerializeAll { case_style, kw } => { @@ -48,6 +50,17 @@ impl HasTypeProperties for DeriveInput { ascii_case_insensitive_kw = Some(kw); output.ascii_case_insensitive = true; } + EnumMeta::Crate { + crate_module_path, + kw, + } => { + if let Some(fst_kw) = crate_module_path_kw { + return Err(occurrence_error(fst_kw, kw, "Crate")); + } + + crate_module_path_kw = Some(kw); + output.crate_module_path = Some(crate_module_path); + } } } @@ -83,3 +96,13 @@ impl HasTypeProperties for DeriveInput { Ok(output) } } + +impl StrumTypeProperties { + pub fn crate_module_path(&self) -> Path { + if let Some(path) = &self.crate_module_path { + parse_quote!(#path) + } else { + parse_quote!(::strum) + } + } +} diff --git a/strum_macros/src/macros/enum_count.rs b/strum_macros/src/macros/enum_count.rs index ada3a0fd..44c7f2e5 100644 --- a/strum_macros/src/macros/enum_count.rs +++ b/strum_macros/src/macros/enum_count.rs @@ -2,13 +2,15 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput}; -use crate::helpers::non_enum_error; +use crate::helpers::{non_enum_error, HasTypeProperties}; pub(crate) fn enum_count_inner(ast: &DeriveInput) -> syn::Result { let n = match &ast.data { Data::Enum(v) => v.variants.len(), _ => return Err(non_enum_error()), }; + let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); // Used in the quasi-quotation below as `#name` let name = &ast.ident; @@ -18,7 +20,7 @@ pub(crate) fn enum_count_inner(ast: &DeriveInput) -> syn::Result { Ok(quote! { // Implementation - impl #impl_generics ::strum::EnumCount for #name #ty_generics #where_clause { + impl #impl_generics #strum_module_path::EnumCount for #name #ty_generics #where_clause { const COUNT: usize = #n; } }) diff --git a/strum_macros/src/macros/enum_iter.rs b/strum_macros/src/macros/enum_iter.rs index b9b2fa3c..a006d85d 100644 --- a/strum_macros/src/macros/enum_iter.rs +++ b/strum_macros/src/macros/enum_iter.rs @@ -2,13 +2,15 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{Data, DeriveInput, Ident}; -use crate::helpers::{non_enum_error, HasStrumVariantProperties}; +use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result { let name = &ast.ident; let gen = &ast.generics; let (impl_generics, ty_generics, where_clause) = gen.split_for_impl(); let vis = &ast.vis; + let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); if gen.lifetimes().count() > 0 { return Err(syn::Error::new( @@ -80,7 +82,7 @@ pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result { } } - impl #impl_generics ::strum::IntoEnumIterator for #name #ty_generics #where_clause { + impl #impl_generics #strum_module_path::IntoEnumIterator for #name #ty_generics #where_clause { type Iterator = #iter_name #ty_generics; fn iter() -> #iter_name #ty_generics { #iter_name { diff --git a/strum_macros/src/macros/enum_messages.rs b/strum_macros/src/macros/enum_messages.rs index 19ff75cf..6e599d02 100644 --- a/strum_macros/src/macros/enum_messages.rs +++ b/strum_macros/src/macros/enum_messages.rs @@ -13,6 +13,7 @@ pub fn enum_message_inner(ast: &DeriveInput) -> syn::Result { }; let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); let mut arms = Vec::new(); let mut detailed_arms = Vec::new(); @@ -79,7 +80,7 @@ pub fn enum_message_inner(ast: &DeriveInput) -> syn::Result { } Ok(quote! { - impl #impl_generics ::strum::EnumMessage for #name #ty_generics #where_clause { + impl #impl_generics #strum_module_path::EnumMessage for #name #ty_generics #where_clause { fn get_message(&self) -> ::core::option::Option<&'static str> { match self { #(#arms),* diff --git a/strum_macros/src/macros/enum_properties.rs b/strum_macros/src/macros/enum_properties.rs index d1f03dae..58265a74 100644 --- a/strum_macros/src/macros/enum_properties.rs +++ b/strum_macros/src/macros/enum_properties.rs @@ -2,7 +2,7 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput}; -use crate::helpers::{non_enum_error, HasStrumVariantProperties}; +use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties}; pub fn enum_properties_inner(ast: &DeriveInput) -> syn::Result { let name = &ast.ident; @@ -11,6 +11,8 @@ pub fn enum_properties_inner(ast: &DeriveInput) -> syn::Result { Data::Enum(v) => &v.variants, _ => return Err(non_enum_error()), }; + let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); let mut arms = Vec::new(); for variant in variants { @@ -53,7 +55,7 @@ pub fn enum_properties_inner(ast: &DeriveInput) -> syn::Result { } Ok(quote! { - impl #impl_generics ::strum::EnumProperty for #name #ty_generics #where_clause { + impl #impl_generics #strum_module_path::EnumProperty for #name #ty_generics #where_clause { fn get_str(&self, prop: &str) -> ::core::option::Option<&'static str> { match self { #(#arms),* diff --git a/strum_macros/src/macros/enum_variant_names.rs b/strum_macros/src/macros/enum_variant_names.rs index b99f179d..c54d45dc 100644 --- a/strum_macros/src/macros/enum_variant_names.rs +++ b/strum_macros/src/macros/enum_variant_names.rs @@ -16,6 +16,7 @@ pub fn enum_variant_names_inner(ast: &DeriveInput) -> syn::Result { // Derives for the generated enum let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); let names = variants .iter() @@ -26,7 +27,7 @@ pub fn enum_variant_names_inner(ast: &DeriveInput) -> syn::Result { .collect::>>()?; Ok(quote! { - impl #impl_generics ::strum::VariantNames for #name #ty_generics #where_clause { + impl #impl_generics #strum_module_path::VariantNames for #name #ty_generics #where_clause { const VARIANTS: &'static [&'static str] = &[ #(#names),* ]; } }) diff --git a/strum_macros/src/macros/strings/as_ref_str.rs b/strum_macros/src/macros/strings/as_ref_str.rs index ca17abe2..b487b36d 100644 --- a/strum_macros/src/macros/strings/as_ref_str.rs +++ b/strum_macros/src/macros/strings/as_ref_str.rs @@ -75,6 +75,8 @@ pub fn as_static_str_inner( let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let arms = get_arms(ast)?; + let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); let mut generics = ast.generics.clone(); generics @@ -88,7 +90,7 @@ pub fn as_static_str_inner( Ok(match trait_variant { GenerateTraitVariant::AsStaticStr => quote! { - impl #impl_generics ::strum::AsStaticRef for #name #ty_generics #where_clause { + impl #impl_generics #strum_module_path::AsStaticRef for #name #ty_generics #where_clause { fn as_static(&self) -> &'static str { match *self { #(#arms),* diff --git a/strum_macros/src/macros/strings/from_string.rs b/strum_macros/src/macros/strings/from_string.rs index 0ca6b9b8..2977fd19 100644 --- a/strum_macros/src/macros/strings/from_string.rs +++ b/strum_macros/src/macros/strings/from_string.rs @@ -15,10 +15,11 @@ pub fn from_string_inner(ast: &DeriveInput) -> syn::Result { }; let type_properties = ast.get_type_properties()?; + let strum_module_path = type_properties.crate_module_path(); let mut default_kw = None; let mut default = - quote! { _ => ::std::result::Result::Err(::strum::ParseError::VariantNotFound) }; + quote! { _ => ::std::result::Result::Err(#strum_module_path::ParseError::VariantNotFound) }; let mut arms = Vec::new(); for variant in variants { let ident = &variant.ident; @@ -89,7 +90,7 @@ pub fn from_string_inner(ast: &DeriveInput) -> syn::Result { Ok(quote! { #[allow(clippy::use_self)] impl #impl_generics ::std::str::FromStr for #name #ty_generics #where_clause { - type Err = ::strum::ParseError; + type Err = #strum_module_path::ParseError; fn from_str(s: &str) -> ::std::result::Result< #name #ty_generics , Self::Err> { match s { #(#arms),* diff --git a/strum_tests/tests/enum_count.rs b/strum_tests/tests/enum_count.rs index 4f03de0e..794e5638 100644 --- a/strum_tests/tests/enum_count.rs +++ b/strum_tests/tests/enum_count.rs @@ -16,3 +16,27 @@ fn simple_test() { assert_eq!(7, Week::COUNT); assert_eq!(Week::iter().count(), Week::COUNT); } + +#[test] +fn crate_module_path_test() { + pub mod nested { + pub mod module { + pub use strum; + } + } + + #[derive(Debug, EnumCount, EnumIter)] + #[strum(crate = "nested::module::strum")] + enum Week { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + } + + assert_eq!(7, Week::COUNT); + assert_eq!(Week::iter().count(), Week::COUNT); +} diff --git a/strum_tests/tests/enum_discriminants.rs b/strum_tests/tests/enum_discriminants.rs index 69026ba8..a9233db8 100644 --- a/strum_tests/tests/enum_discriminants.rs +++ b/strum_tests/tests/enum_discriminants.rs @@ -279,3 +279,26 @@ fn override_visibility() { private::PubDiscriminants::VariantB, ); } + +#[test] +fn crate_module_path_test() { + pub mod nested { + pub mod module { + pub use strum; + } + } + + #[allow(dead_code)] + #[derive(Debug, Eq, PartialEq, EnumDiscriminants)] + #[strum_discriminants(derive(EnumIter))] + #[strum(crate = "nested::module::strum")] + enum Simple { + Variant0, + Variant1, + } + + let discriminants = SimpleDiscriminants::iter().collect::>(); + let expected = vec![SimpleDiscriminants::Variant0, SimpleDiscriminants::Variant1]; + + assert_eq!(expected, discriminants); +} diff --git a/strum_tests/tests/enum_iter.rs b/strum_tests/tests/enum_iter.rs index 35ec9eb3..7468267a 100644 --- a/strum_tests/tests/enum_iter.rs +++ b/strum_tests/tests/enum_iter.rs @@ -175,3 +175,37 @@ fn take_nth_test() { assert_eq!(None, iter.next()); assert_eq!(None, iter.next_back()); } + +#[test] +fn crate_module_path_test() { + pub mod nested { + pub mod module { + pub use strum; + } + } + + #[derive(Debug, Eq, PartialEq, EnumIter)] + #[strum(crate = "nested::module::strum")] + enum Week { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + } + + let results = Week::iter().collect::>(); + let expected = vec![ + Week::Sunday, + Week::Monday, + Week::Tuesday, + Week::Wednesday, + Week::Thursday, + Week::Friday, + Week::Saturday, + ]; + + assert_eq!(expected, results); +} diff --git a/strum_tests/tests/enum_message.rs b/strum_tests/tests/enum_message.rs index 1d3de33c..b448ac05 100644 --- a/strum_tests/tests/enum_message.rs +++ b/strum_tests/tests/enum_message.rs @@ -76,3 +76,31 @@ fn get_serializations() { (Brightness::BrightWhite).get_serializations() ); } + +#[test] +fn crate_module_path_test() { + pub mod nested { + pub mod module { + pub use strum; + } + } + + #[allow(dead_code)] + #[derive(Debug, Eq, PartialEq, EnumMessage)] + #[strum(crate = "nested::module::strum")] + enum Pets { + #[strum(message = "I'm a dog")] + Dog, + #[strum(message = "I'm a cat")] + #[strum(detailed_message = "I'm a very exquisite striped cat")] + Cat, + #[strum(detailed_message = "My fish is named Charles McFish")] + Fish, + Bird, + #[strum(disabled)] + Hamster, + } + + assert_eq!("I'm a dog", (Pets::Dog).get_message().unwrap()); + assert_eq!("I'm a dog", (Pets::Dog).get_detailed_message().unwrap()); +} diff --git a/strum_tests/tests/enum_props.rs b/strum_tests/tests/enum_props.rs index 55be0f63..68fbb4d5 100644 --- a/strum_tests/tests/enum_props.rs +++ b/strum_tests/tests/enum_props.rs @@ -24,3 +24,24 @@ fn prop_test_not_found_2() { let b = Test::B; assert_eq!(None, b.get_str("key")); } + +#[test] +fn crate_module_path_test() { + pub mod nested { + pub mod module { + pub use strum; + } + } + + #[allow(dead_code)] + #[derive(Debug, EnumProperty)] + #[strum(crate = "nested::module::strum")] + enum Test { + #[strum(props(key = "value"))] + A, + B, + } + + let a = Test::A; + assert_eq!("value", a.get_str("key").unwrap()); +} diff --git a/strum_tests/tests/enum_variant_names.rs b/strum_tests/tests/enum_variant_names.rs index f9c8889f..54ee857c 100644 --- a/strum_tests/tests/enum_variant_names.rs +++ b/strum_tests/tests/enum_variant_names.rs @@ -104,3 +104,25 @@ fn clap_and_structopt() { color: Color, } } + +#[test] +fn crate_module_path_test() { + pub mod nested { + pub mod module { + pub use strum; + } + } + + #[allow(dead_code)] + #[derive(EnumVariantNames)] + #[strum(crate = "nested::module::strum")] + enum Color { + Red, + #[strum(serialize = "b")] + Blue, + #[strum(to_string = "y", serialize = "yy")] + Yellow, + } + + assert_eq!(Color::VARIANTS, &["Red", "b", "y"]); +}