Repository: Peternator7/strum
Pull request #293 (2023-08-28 22:04:17)

fix EnumIter for enums with const generic
Currently, EnumIter for struct with constant generic results in error E0747 (constant provided when a type was expected).
That's because generated iter struct looks sth like:
```Rust
struct LanguageIter<LAUNCHER> {
        idx: usize,
        back_idx: usize,
        marker: ::core::marker::PhantomData<()>,
    }
```
for enum:
```Rust
#[derive( EnumIter)]
enum Language<const LAUNCHER: bool> {...}
```
Which is ok for ordinary(type) generics, but for const generics iter struct definition has to look like:
```Rust
struct LanguageIter<const LAUNCHER: bool> {...}
```
this PR makes EnumIter work with const generics, by using $impl_generics instead of $ty_generics for struct definition.
Also added const generic to enum in tests.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f220d6..993b915 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Changelog
 
+## 0.25.3 (strum_macros)
+
+Received a number of bug fix PR's.
+
+* [#300](https://github.com/Peternator7/strum/pull/300): Fixes EnumIter for enums named `Option`.
+* [#301](https://github.com/Peternator7/strum/pull/301): Improved doc comments
+* [#304](https://github.com/Peternator7/strum/pull/304): Removed some unused Vec's
+* [#305](https://github.com/Peternator7/strum/pull/305): Added generic support to `EnumIs`
+
 ## 0.25.2 (strum_macros)
 
 * [#289](https://github.com/Peternator7/strum/pull/289): Enables a previously disabled rustdoc.
diff --git a/strum/src/lib.rs b/strum/src/lib.rs
index 7fbe5e2..78a7020 100644
--- a/strum/src/lib.rs
+++ b/strum/src/lib.rs
@@ -30,6 +30,8 @@
 // only for documentation purposes
 pub mod additional_attributes;
 
+use core::iter::FusedIterator;
+
 #[cfg(feature = "phf")]
 #[doc(hidden)]
 pub use phf as _private_phf_reexport_for_macro_if_phf_feature;
@@ -96,7 +98,7 @@ impl std::error::Error for ParseError {
 /// generic_iterator::<Color, _>(|color| println!("{:?}", color));
 /// ```
 pub trait IntoEnumIterator: Sized {
-    type Iterator: Iterator<Item = Self>;
+    type Iterator: Iterator<Item = Self> + Clone + DoubleEndedIterator + ExactSizeIterator + FusedIterator;
 
     fn iter() -> Self::Iterator;
 }
diff --git a/strum_macros/Cargo.toml b/strum_macros/Cargo.toml
index c1b9762..533e730 100644
--- a/strum_macros/Cargo.toml
+++ b/strum_macros/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "strum_macros"
-version = "0.25.2"
+version = "0.25.3"
 edition = "2018"
 authors = ["Peter Glotfelty <peter.glotfelty@microsoft.com>"]
 license = "MIT"
diff --git a/strum_macros/src/helpers/case_style.rs b/strum_macros/src/helpers/case_style.rs
index 86a8583..bcea788 100644
--- a/strum_macros/src/helpers/case_style.rs
+++ b/strum_macros/src/helpers/case_style.rs
@@ -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::{
diff --git a/strum_macros/src/helpers/metadata.rs b/strum_macros/src/helpers/metadata.rs
index d638ae3..19eb927 100644
--- a/strum_macros/src/helpers/metadata.rs
+++ b/strum_macros/src/helpers/metadata.rs
@@ -17,6 +17,7 @@ pub mod kw {
     // enum metadata
     custom_keyword!(serialize_all);
     custom_keyword!(use_phf);
+    custom_keyword!(prefix);
 
     // enum discriminant metadata
     custom_keyword!(derive);
@@ -45,6 +46,10 @@ pub enum EnumMeta {
         crate_module_path: Path,
     },
     UsePhf(kw::use_phf),
+    Prefix {
+        kw: kw::prefix,
+        prefix: LitStr,
+    },
 }
 
 impl Parse for EnumMeta {
@@ -69,6 +74,11 @@ impl Parse for EnumMeta {
             Ok(EnumMeta::AsciiCaseInsensitive(input.parse()?))
         } else if lookahead.peek(kw::use_phf) {
             Ok(EnumMeta::UsePhf(input.parse()?))
+        } else if lookahead.peek(kw::prefix) {
+            let kw = input.parse::<kw::prefix>()?;
+            input.parse::<Token![=]>()?;
+            let prefix = input.parse()?;
+            Ok(EnumMeta::Prefix { kw, prefix })
         } else {
             Err(lookahead.error())
         }
diff --git a/strum_macros/src/helpers/mod.rs b/strum_macros/src/helpers/mod.rs
index 142ea0b..724fce2 100644
--- a/strum_macros/src/helpers/mod.rs
+++ b/strum_macros/src/helpers/mod.rs
@@ -1,4 +1,4 @@
-pub use self::case_style::{CaseStyleHelpers, snakify};
+pub use self::case_style::{snakify, CaseStyleHelpers};
 pub use self::type_props::HasTypeProperties;
 pub use self::variant_props::HasStrumVariantProperties;
 
diff --git a/strum_macros/src/helpers/type_props.rs b/strum_macros/src/helpers/type_props.rs
index 0d49e04..7302853 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::{parse_quote, DeriveInput, Ident, Path, Visibility};
+use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility};
 
 use super::case_style::CaseStyle;
 use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta};
@@ -21,6 +21,8 @@ pub struct StrumTypeProperties {
     pub discriminant_others: Vec<TokenStream>,
     pub discriminant_vis: Option<Visibility>,
     pub use_phf: bool,
+    pub prefix: Option<LitStr>,
+    pub enum_repr: Option<TokenStream>,
 }
 
 impl HasTypeProperties for DeriveInput {
@@ -34,6 +36,7 @@ impl HasTypeProperties for DeriveInput {
         let mut ascii_case_insensitive_kw = None;
         let mut use_phf_kw = None;
         let mut crate_module_path_kw = None;
+        let mut prefix_kw = None;
         for meta in strum_meta {
             match meta {
                 EnumMeta::SerializeAll { case_style, kw } => {
@@ -71,6 +74,14 @@ impl HasTypeProperties for DeriveInput {
                     crate_module_path_kw = Some(kw);
                     output.crate_module_path = Some(crate_module_path);
                 }
+                EnumMeta::Prefix { prefix, kw } => {
+                    if let Some(fst_kw) = prefix_kw {
+                        return Err(occurrence_error(fst_kw, kw, "prefix"));
+                    }
+
+                    prefix_kw = Some(kw);
+                    output.prefix = Some(prefix);
+                }
             }
         }
 
@@ -103,6 +114,17 @@ impl HasTypeProperties for DeriveInput {
             }
         }
 
+        let attrs = &self.attrs;
+        for attr in attrs {
+            if let Ok(list) = attr.meta.require_list() {
+                if let Some(ident) = list.path.get_ident() {
+                    if ident == "repr" {
+                        output.enum_repr = Some(list.tokens.clone())
+                    }
+                }
+            }
+        }
+
         Ok(output)
     }
 }
diff --git a/strum_macros/src/lib.rs b/strum_macros/src/lib.rs
index 82db12a..673419a 100644
--- a/strum_macros/src/lib.rs
+++ b/strum_macros/src/lib.rs
@@ -44,7 +44,7 @@ fn debug_print_generated(ast: &DeriveInput, toks: &TokenStream) {
 /// variant. There is an option to match on different case conversions through the
 /// `#[strum(serialize_all = "snake_case")]` type attribute.
 ///
-/// See the [Additional Attributes](https://docs.rs/strum/0.22/strum/additional_attributes/index.html)
+/// See the [Additional Attributes](https://docs.rs/strum/latest/strum/additional_attributes/index.html)
 /// Section for more information on using this feature.
 ///
 /// If you have a large enum, you may want to consider using the `use_phf` attribute here. It leverages
diff --git a/strum_macros/src/macros/enum_discriminants.rs b/strum_macros/src/macros/enum_discriminants.rs
index 4e54a30..f7d6372 100644
--- a/strum_macros/src/macros/enum_discriminants.rs
+++ b/strum_macros/src/macros/enum_discriminants.rs
@@ -40,10 +40,16 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
     // Pass through all other attributes
     let pass_though_attributes = type_properties.discriminant_others;
 
+    let repr = type_properties.enum_repr.map(|repr| quote!(#[repr(#repr)]));
+
     // Add the variants without fields, but exclude the `strum` meta item
     let mut discriminants = Vec::new();
     for variant in variants {
         let ident = &variant.ident;
+        let discriminant = variant
+            .discriminant
+            .as_ref()
+            .map(|(_, expr)| quote!( = #expr));
 
         // Don't copy across the "strum" meta attribute. Only passthrough the whitelisted
         // attributes and proxy `#[strum_discriminants(...)]` attributes
@@ -81,7 +87,7 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
             })
             .collect::<Result<Vec<_>, _>>()?;
 
-        discriminants.push(quote! { #(#attrs)* #ident });
+        discriminants.push(quote! { #(#attrs)* #ident #discriminant});
     }
 
     // Ideally:
@@ -153,6 +159,7 @@ pub fn enum_discriminants_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
     Ok(quote! {
         /// Auto-generated discriminant enum variants
         #derives
+        #repr
         #(#[ #pass_though_attributes ])*
         #discriminants_vis enum #discriminants_name {
             #(#discriminants),*
diff --git a/strum_macros/src/macros/enum_is.rs b/strum_macros/src/macros/enum_is.rs
index b69fa0c..ecada45 100644
--- a/strum_macros/src/macros/enum_is.rs
+++ b/strum_macros/src/macros/enum_is.rs
@@ -8,9 +8,9 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         Data::Enum(v) => &v.variants,
         _ => return Err(non_enum_error()),
     };
+    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
 
     let enum_name = &ast.ident;
-
     let variants: Vec<_> = variants
         .iter()
         .filter_map(|variant| {
@@ -20,10 +20,11 @@ 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);
             Some(quote! {
                 #[must_use]
                 #[inline]
+                #[doc = #doc_comment]
                 pub const fn #fn_name(&self) -> bool {
                     match self {
                         &#enum_name::#variant_name { .. } => true,
@@ -35,7 +36,7 @@ pub fn enum_is_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         .collect();
 
     Ok(quote! {
-        impl #enum_name {
+        impl #impl_generics #enum_name  #ty_generics #where_clause {
             #(#variants)*
         }
     }
diff --git a/strum_macros/src/macros/enum_iter.rs b/strum_macros/src/macros/enum_iter.rs
index ce8237f..5e81001 100644
--- a/strum_macros/src/macros/enum_iter.rs
+++ b/strum_macros/src/macros/enum_iter.rs
@@ -74,7 +74,7 @@ pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         #[allow(
             missing_copy_implementations,
         )]
-        #vis struct #iter_name #ty_generics {
+        #vis struct #iter_name #impl_generics {
             idx: usize,
             back_idx: usize,
             marker: ::core::marker::PhantomData #phantom_data,
@@ -91,7 +91,7 @@ pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         }
 
         impl #impl_generics #iter_name #ty_generics #where_clause {
-            fn get(&self, idx: usize) -> Option<#name #ty_generics> {
+            fn get(&self, idx: usize) -> ::core::option::Option<#name #ty_generics> {
                 match idx {
                     #(#arms),*
                 }
@@ -112,16 +112,16 @@ pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         impl #impl_generics Iterator for #iter_name #ty_generics #where_clause {
             type Item = #name #ty_generics;
 
-            fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+            fn next(&mut self) -> ::core::option::Option<<Self as Iterator>::Item> {
                 self.nth(0)
             }
 
-            fn size_hint(&self) -> (usize, Option<usize>) {
+            fn size_hint(&self) -> (usize, ::core::option::Option<usize>) {
                 let t = if self.idx + self.back_idx >= #variant_count { 0 } else { #variant_count - self.idx - self.back_idx };
                 (t, Some(t))
             }
 
-            fn nth(&mut self, n: usize) -> Option<<Self as Iterator>::Item> {
+            fn nth(&mut self, n: usize) -> ::core::option::Option<<Self as Iterator>::Item> {
                 let idx = self.idx + n + 1;
                 if idx + self.back_idx > #variant_count {
                     // We went past the end of the iterator. Freeze idx at #variant_count
@@ -143,7 +143,7 @@ pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         }
 
         impl #impl_generics DoubleEndedIterator for #iter_name #ty_generics #where_clause {
-            fn next_back(&mut self) -> Option<<Self as Iterator>::Item> {
+            fn next_back(&mut self) -> ::core::option::Option<<Self as Iterator>::Item> {
                 let back_idx = self.back_idx + 1;
 
                 if self.idx + back_idx > #variant_count {
@@ -159,6 +159,8 @@ pub fn enum_iter_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
             }
         }
 
+        impl #impl_generics ::core::iter::FusedIterator for #iter_name #ty_generics #where_clause { }
+
         impl #impl_generics Clone for #iter_name #ty_generics #where_clause {
             fn clone(&self) -> #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 c056108..2dad411 100644
--- a/strum_macros/src/macros/enum_messages.rs
+++ b/strum_macros/src/macros/enum_messages.rs
@@ -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
diff --git a/strum_macros/src/macros/enum_properties.rs b/strum_macros/src/macros/enum_properties.rs
index 0fe389e..2583096 100644
--- a/strum_macros/src/macros/enum_properties.rs
+++ b/strum_macros/src/macros/enum_properties.rs
@@ -19,8 +19,6 @@ pub fn enum_properties_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         let ident = &variant.ident;
         let variant_properties = variant.get_variant_properties()?;
         let mut string_arms = Vec::new();
-        let mut bool_arms = Vec::new();
-        let mut num_arms = Vec::new();
         // But you can disable the messages.
         if variant_properties.disabled.is_some() {
             continue;
@@ -37,8 +35,6 @@ pub fn enum_properties_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         }
 
         string_arms.push(quote! { _ => ::core::option::Option::None });
-        bool_arms.push(quote! { _ => ::core::option::Option::None });
-        num_arms.push(quote! { _ => ::core::option::Option::None });
 
         arms.push(quote! {
             &#name::#ident #params => {
diff --git a/strum_macros/src/macros/enum_variant_names.rs b/strum_macros/src/macros/enum_variant_names.rs
index c54d45d..f53b0af 100644
--- a/strum_macros/src/macros/enum_variant_names.rs
+++ b/strum_macros/src/macros/enum_variant_names.rs
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{Data, DeriveInput};
+use syn::{Data, DeriveInput, LitStr};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -24,7 +24,7 @@ pub fn enum_variant_names_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
             let props = v.get_variant_properties()?;
             Ok(props.get_preferred_name(type_properties.case_style))
         })
-        .collect::<syn::Result<Vec<_>>>()?;
+        .collect::<syn::Result<Vec<LitStr>>>()?;
 
     Ok(quote! {
         impl #impl_generics #strum_module_path::VariantNames for #name #ty_generics #where_clause {
diff --git a/strum_macros/src/macros/from_repr.rs b/strum_macros/src/macros/from_repr.rs
index 92fd7ad..a70c6f8 100644
--- a/strum_macros/src/macros/from_repr.rs
+++ b/strum_macros/src/macros/from_repr.rs
@@ -1,62 +1,32 @@
 use heck::ToShoutySnakeCase;
 use proc_macro2::{Span, TokenStream};
-use quote::{format_ident, quote, ToTokens};
-use syn::{Data, DeriveInput, Fields, PathArguments, Type, TypeParen};
+use quote::{format_ident, quote};
+use syn::{Data, DeriveInput, Fields, Type};
 
-use crate::helpers::{non_enum_error, HasStrumVariantProperties};
+use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
 pub fn from_repr_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
     let name = &ast.ident;
     let gen = &ast.generics;
     let (impl_generics, ty_generics, where_clause) = gen.split_for_impl();
     let vis = &ast.vis;
-    let attrs = &ast.attrs;
 
     let mut discriminant_type: Type = syn::parse("usize".parse().unwrap()).unwrap();
-    for attr in attrs {
-        let path = attr.path();
-
-        let mut ts = if let Ok(ts) = attr
-            .meta
-            .require_list()
-            .map(|metas| metas.to_token_stream().into_iter())
-        {
-            ts
-        } else {
-            continue;
-        };
-        // Discard the path
-        let _ = ts.next();
-        let tokens: TokenStream = ts.collect();
-
-        if path.leading_colon.is_some() {
-            continue;
-        }
-        if path.segments.len() != 1 {
-            continue;
-        }
-        let segment = path.segments.first().unwrap();
-        if segment.ident != "repr" {
-            continue;
-        }
-        if segment.arguments != PathArguments::None {
-            continue;
-        }
-        let typ_paren = match syn::parse2::<Type>(tokens.clone()) {
-            Ok(Type::Paren(TypeParen { elem, .. })) => *elem,
-            _ => continue,
-        };
-        let inner_path = match &typ_paren {
-            Type::Path(t) => t,
-            _ => continue,
-        };
-        if let Some(seg) = inner_path.path.segments.last() {
-            for t in &[
-                "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize",
-            ] {
-                if seg.ident == t {
-                    discriminant_type = typ_paren;
-                    break;
+    if let Some(type_path) = ast
+        .get_type_properties()
+        .ok()
+        .and_then(|tp| tp.enum_repr)
+        .and_then(|repr_ts| syn::parse2::<Type>(repr_ts).ok())
+    {
+        if let Type::Path(path) = type_path.clone() {
+            if let Some(seg) = path.path.segments.last() {
+                for t in &[
+                    "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize",
+                ] {
+                    if seg.ident == t {
+                        discriminant_type = type_path;
+                        break;
+                    }
                 }
             }
         }
diff --git a/strum_macros/src/macros/strings/display.rs b/strum_macros/src/macros/strings/display.rs
index fcc5936..2fd0eef 100644
--- a/strum_macros/src/macros/strings/display.rs
+++ b/strum_macros/src/macros/strings/display.rs
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{Data, DeriveInput, Fields};
+use syn::{Data, DeriveInput, Fields, LitStr};
 
 use crate::helpers::{non_enum_error, HasStrumVariantProperties, HasTypeProperties};
 
@@ -24,7 +24,10 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         }
 
         // Look at all the serialize attributes.
-        let output = variant_properties.get_preferred_name(type_properties.case_style);
+        let mut output = variant_properties.get_preferred_name(type_properties.case_style);
+        if let Some(prefix) = &type_properties.prefix {
+            output = LitStr::new(&(prefix.value() + &output.value()), output.span());
+        }
 
         let params = match variant.fields {
             Fields::Unit => quote! {},
@@ -35,7 +38,7 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
         if variant_properties.to_string.is_none() && variant_properties.default.is_some() {
             match &variant.fields {
                 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
-                    arms.push(quote! { #name::#ident(ref s) => f.pad(s) });
+                    arms.push(quote! { #name::#ident(ref s) => ::core::fmt::Display::fmt(s, f) });
                 }
                 _ => {
                     return Err(syn::Error::new_spanned(
@@ -45,7 +48,7 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
                 }
             }
         } else {
-            arms.push(quote! { #name::#ident #params => f.pad(#output) });
+            arms.push(quote! { #name::#ident #params => ::core::fmt::Display::fmt(#output, f) } );
         }
     }
 
diff --git a/strum_macros/src/macros/strings/from_string.rs b/strum_macros/src/macros/strings/from_string.rs
index 2d25591..4b9a721 100644
--- a/strum_macros/src/macros/strings/from_string.rs
+++ b/strum_macros/src/macros/strings/from_string.rs
@@ -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 =


Test patch:
diff --git a/strum_tests/src/lib.rs b/strum_tests/src/lib.rs
index 3addd0d..eb3f65d 100644
--- a/strum_tests/src/lib.rs
+++ b/strum_tests/src/lib.rs
@@ -1,6 +1,7 @@
 use strum::{Display, EnumCount, EnumDiscriminants, EnumString};
+use strum_macros::EnumIs;
 
-#[derive(Debug, Eq, PartialEq, EnumString, Display, EnumCount, EnumDiscriminants)]
+#[derive(Debug, Eq, PartialEq, EnumString, Display, EnumCount, EnumDiscriminants, EnumIs)]
 pub enum Color {
     /// Docs on red
     #[strum(to_string = "RedRed")]
diff --git a/strum_tests/src/main.rs b/strum_tests/src/main.rs
index 4d048a6..97e348a 100644
--- a/strum_tests/src/main.rs
+++ b/strum_tests/src/main.rs
@@ -1,7 +1,8 @@
 use strum::{Display, EnumCount, EnumDiscriminants, EnumString};
+use strum_macros::EnumIs;
 
 #[allow(dead_code)]
-#[derive(Debug, Eq, PartialEq, EnumString, Display, EnumCount, EnumDiscriminants)]
+#[derive(Debug, Eq, PartialEq, EnumString, Display, EnumCount, EnumDiscriminants, EnumIs)]
 enum Color {
     /// Random Docs
     #[strum(to_string = "RedRed")]
diff --git a/strum_tests/tests/display.rs b/strum_tests/tests/display.rs
index 4cec148..77ea25c 100644
--- a/strum_tests/tests/display.rs
+++ b/strum_tests/tests/display.rs
@@ -1,8 +1,8 @@
-use strum::{Display, EnumString};
+use strum::EnumString;
 
 mod core {} // ensure macros call `::core`
 
-#[derive(Debug, Eq, PartialEq, EnumString, Display)]
+#[derive(Debug, Eq, PartialEq, EnumString, strum::Display)]
 enum Color {
     #[strum(to_string = "RedRed")]
     Red,
@@ -49,12 +49,12 @@ fn to_red_string() {
 #[test]
 fn to_green_string() {
     assert_eq!(
-        String::from("lime"),
-        format!("{}", Color::Green("lime".into()))
+        String::from("  lime"),
+        format!("{:>6}", Color::Green("lime".into()))
     );
 }
 
-#[derive(Debug, Eq, PartialEq, EnumString, Display)]
+#[derive(Debug, Eq, PartialEq, EnumString, strum::Display)]
 enum ColorWithDefaultAndToString {
     #[strum(default, to_string = "GreenGreen")]
     Green(String),
@@ -68,7 +68,7 @@ fn to_green_with_default_and_to_string() {
     );
 }
 
-#[derive(Display, Debug, Eq, PartialEq)]
+#[derive(strum::Display, Debug, Eq, PartialEq)]
 #[strum(serialize_all = "snake_case")]
 enum Brightness {
     DarkBlack,
@@ -94,3 +94,18 @@ fn brightness_to_string() {
         Brightness::BrightWhite.to_string().as_ref()
     );
 }
+
+#[derive(strum::Display, Debug, Eq, PartialEq)]
+#[strum(serialize_all = "snake_case")]
+enum NonStringDefault {
+    #[strum(default)]
+    Number(usize),
+}
+
+#[test]
+fn non_string_default_to_string() {
+    assert_eq!(
+        String::from("0014"),
+        format!("{:04}", NonStringDefault::Number(14))
+    );
+}
diff --git a/strum_tests/tests/enum_discriminants.rs b/strum_tests/tests/enum_discriminants.rs
index eb29e6d..d6e4668 100644
--- a/strum_tests/tests/enum_discriminants.rs
+++ b/strum_tests/tests/enum_discriminants.rs
@@ -1,6 +1,9 @@
-use enum_variant_type::EnumVariantType;
-use strum::{Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, IntoEnumIterator};
+use std::mem::{align_of, size_of};
 
+use enum_variant_type::EnumVariantType;
+use strum::{
+    Display, EnumDiscriminants, EnumIter, EnumMessage, EnumString, FromRepr, IntoEnumIterator,
+};
 
 mod core {} // ensure macros call `::core`
 
@@ -305,3 +308,58 @@ fn crate_module_path_test() {
 
     assert_eq!(expected, discriminants);
 }
+
+#[allow(dead_code)]
+#[derive(EnumDiscriminants)]
+#[repr(u16)]
+enum WithReprUInt {
+    Variant0,
+    Variant1,
+}
+
+#[test]
+fn with_repr_uint() {
+    // These tests would not be proof of proper functioning on a 16 bit system
+    assert_eq!(size_of::<u16>(), size_of::<WithReprUIntDiscriminants>());
+    assert_eq!(
+        size_of::<WithReprUInt>(),
+        size_of::<WithReprUIntDiscriminants>()
+    )
+}
+
+#[allow(dead_code)]
+#[derive(EnumDiscriminants)]
+#[repr(align(16), u8)]
+enum WithReprAlign {
+    Variant0,
+    Variant1,
+}
+
+#[test]
+fn with_repr_align() {
+    assert_eq!(
+        align_of::<WithReprAlign>(),
+        align_of::<WithReprAlignDiscriminants>()
+    );
+    assert_eq!(16, align_of::<WithReprAlignDiscriminants>());
+}
+
+#[allow(dead_code)]
+#[derive(EnumDiscriminants)]
+#[strum_discriminants(derive(FromRepr))]
+enum WithExplicitDicriminantValue {
+    Variant0 = 42 + 100,
+    Variant1 = 11,
+}
+
+#[test]
+fn with_explicit_discriminant_value() {
+    assert_eq!(
+        WithExplicitDicriminantValueDiscriminants::from_repr(11),
+        Some(WithExplicitDicriminantValueDiscriminants::Variant1)
+    );
+    assert_eq!(
+        142,
+        WithExplicitDicriminantValueDiscriminants::Variant0 as u8
+    );
+}
diff --git a/strum_tests/tests/enum_is.rs b/strum_tests/tests/enum_is.rs
index 789881c..3226ebb 100644
--- a/strum_tests/tests/enum_is.rs
+++ b/strum_tests/tests/enum_is.rs
@@ -1,13 +1,23 @@
+use std::borrow::Cow;
 use strum::EnumIs;
 
 mod core {} // ensure macros call `::core`
-
+#[derive(EnumIs)]
+enum LifeTimeTest<'a>{
+    One(Cow<'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),
@@ -16,7 +26,15 @@ enum Foo {
     #[allow(dead_code)]
     Disabled,
 }
-
+#[test]
+fn generics_test(){
+    let foo = LifeTimeTest::One(Cow::Borrowed("Hello"));
+    assert!(foo.is_one());
+    let foo = LifeTimeTest::Two("Hello");
+    assert!(foo.is_two());
+    let foo = LifeTimeTest::One(Cow::Owned("Hello".to_string()));
+    assert!(foo.is_one());
+}
 #[test]
 fn simple_test() {
     assert!(Foo::Unit.is_unit());
diff --git a/strum_tests/tests/enum_iter.rs b/strum_tests/tests/enum_iter.rs
index 313ed5c..8fec64b 100644
--- a/strum_tests/tests/enum_iter.rs
+++ b/strum_tests/tests/enum_iter.rs
@@ -30,7 +30,7 @@ fn simple_test() {
 }
 
 #[derive(Debug, Eq, PartialEq, EnumIter)]
-enum Complicated<U: Default, V: Default> {
+enum Complicated<U: Default, V: Default, const Y: usize> {
     A(U),
     B { v: V },
     C,
@@ -40,7 +40,7 @@ enum Complicated<U: Default, V: Default> {
 fn complicated_test() {
     let results = Complicated::iter().collect::<Vec<_>>();
     let expected = vec![
-        Complicated::A(0),
+        Complicated::<_,_,0>::A(0),
         Complicated::B { v: String::new() },
         Complicated::C,
     ];
@@ -50,7 +50,7 @@ fn complicated_test() {
 
 #[test]
 fn len_test() {
-    let mut i = Complicated::<(), ()>::iter();
+    let mut i = Complicated::<(), (), 0>::iter();
     assert_eq!(3, i.len());
     i.next();
 
@@ -68,7 +68,7 @@ fn len_test() {
 
 #[test]
 fn double_ended_len_test() {
-    let mut i = Complicated::<(), ()>::iter();
+    let mut i = Complicated::<(), (), 0>::iter();
     assert_eq!(3, i.len());
     i.next_back();
 
@@ -211,3 +211,16 @@ fn crate_module_path_test() {
 
     assert_eq!(expected, results);
 }
+
+#[test]
+fn enum_iter_option() {
+    #[derive(Debug, Eq, PartialEq, EnumIter)]
+    enum Option {
+        BluePill,
+        RedPill,
+    }
+    let results = Option::iter().collect::<Vec<_>>();
+    let expected = vec![Option::BluePill, Option::RedPill];
+
+    assert_eq!(expected, results);
+}
diff --git a/strum_tests/tests/enum_message.rs b/strum_tests/tests/enum_message.rs
index b1c8600..e7ff303 100644
--- a/strum_tests/tests/enum_message.rs
+++ b/strum_tests/tests/enum_message.rs
@@ -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());
 }
diff --git a/strum_tests/tests/enum_try_as.rs b/strum_tests/tests/enum_try_as.rs
index aa54205..6cbf81e 100644
--- a/strum_tests/tests/enum_try_as.rs
+++ b/strum_tests/tests/enum_try_as.rs
@@ -11,7 +11,10 @@ enum Foo {
     #[allow(dead_code)]
     Unit,
     #[allow(dead_code)]
-    Named { _a: u32, _b: String },
+    Named {
+        _a: u32,
+        _b: String,
+    },
 }
 
 #[test]
diff --git a/strum_tests/tests/prefix.rs b/strum_tests/tests/prefix.rs
new file mode 100644
index 0000000..806b5d5
--- /dev/null
+++ b/strum_tests/tests/prefix.rs
@@ -0,0 +1,20 @@
+use strum::{Display, EnumString};
+
+#[allow(dead_code)]
+#[derive(Debug, EnumString, Display)]
+#[strum(prefix = "colour/")]
+enum Color {
+    #[strum(to_string = "RedRed")]
+    Red,
+    #[strum(serialize = "b", to_string = "blue")]
+    Blue { hue: usize },
+    #[strum(serialize = "y", serialize = "yellow")]
+    Yellow,
+    #[strum(default)]
+    Green(String),
+}
+
+#[test]
+fn prefix_redred() {
+    assert_eq!(String::from("colour/RedRed"), (Color::Red).to_string());
+}