Skip to content

Commit

Permalink
Merge branch 'feat/static-variants-array' of https://github.com/Synde…
Browse files Browse the repository at this point in the history
…lis/strum into Syndelis-feat/static-variants-array
  • Loading branch information
Peter Glotfelty committed Jan 21, 2024
2 parents f0bf858 + cb8b753 commit 2868766
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Strum has implemented the following macros:
| [EnumMessage] | Add a verbose message to an enum variant. |
| [EnumDiscriminants] | Generate a new type with only the discriminant names. |
| [EnumCount] | Add a constant `usize` equal to the number of variants. |
| [StaticVariantsArray] | Adds an associated `ALL_VARIANTS` constant which is an array of all enum discriminants |

# Contributing

Expand Down Expand Up @@ -81,3 +82,4 @@ Strumming is also a very whimsical motion, much like writing Rust code.
[EnumDiscriminants]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumDiscriminants.html
[EnumCount]: https://docs.rs/strum_macros/0.25/strum_macros/derive.EnumCount.html
[FromRepr]: https://docs.rs/strum_macros/0.25/strum_macros/derive.FromRepr.html
[StaticVariantsArray]: https://docs.rs/strum_macros/0.25/strum_macros/derive.StaticVariantsArray.html
13 changes: 12 additions & 1 deletion strum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ pub trait VariantNames {
const VARIANTS: &'static [&'static str];
}

/// A trait for retrieving a static array containing all the variants in an Enum.
/// This trait can be autoderived by `strum_macros`. For derived usage, all the
/// variants in the enumerator need to be unit-types, which means you can't autoderive
/// enums with inner data in one or more variants. Consider using it alongside
/// [`EnumDiscriminants`] if you require inner data but still want to have an
/// static array of variants.
pub trait StaticVariantsArray: std::marker::Sized + 'static {
const ALL_VARIANTS: &'static [Self];
}

#[cfg(feature = "derive")]
pub use strum_macros::*;

Expand Down Expand Up @@ -243,5 +253,6 @@ DocumentMacroRexports! {
EnumVariantNames,
FromRepr,
IntoStaticStr,
ToString
ToString,
StaticVariantsArray
}
2 changes: 1 addition & 1 deletion strum_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ rustversion = "1.0"
syn = { version = "2.0", features = ["parsing", "extra-traits"] }

[dev-dependencies]
strum = "0.25"
strum = { path = "../strum" }
8 changes: 8 additions & 0 deletions strum_macros/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ pub fn non_enum_error() -> syn::Error {
syn::Error::new(Span::call_site(), "This macro only supports enums.")
}

pub fn non_unit_variant_error() -> syn::Error {
syn::Error::new(
Span::call_site(),
"This macro only supports enums of strictly unit variants. Consider \
using it in conjunction with [`EnumDiscriminants`]"
)
}

pub fn strum_discriminants_passthrough_error(span: &impl Spanned) -> syn::Error {
syn::Error::new(
span.span(),
Expand Down
33 changes: 32 additions & 1 deletion strum_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,37 @@ pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream
toks.into()
}

/// Adds a static array with all of the Enum's variants.
///
/// Implements `Strum::StaticVariantsArray` which adds an associated constant `ALL_VARIANTS`.
/// This constant contains an array with all the variants of the enumerator.
///
/// This trait can only be autoderived if the enumerator is composed only of unit-type variants,
/// meaning that the variants must not have any data.
///
/// ```
/// use strum::StaticVariantsArray;
///
/// #[derive(StaticVariantsArray, Debug, PartialEq, Eq)]
/// enum Op {
/// Add,
/// Sub,
/// Mul,
/// Div,
/// }
///
/// assert_eq!(Op::ALL_VARIANTS, &[Op::Add, Op::Sub, Op::Mul, Op::Div]);
/// ```
#[proc_macro_derive(StaticVariantsArray, attributes(strum))]
pub fn static_variants_array(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);

let toks = macros::static_variants_array::static_variants_array_inner(&ast)
.unwrap_or_else(|err| err.to_compile_error());
debug_print_generated(&ast, &toks);
toks.into()
}

#[proc_macro_derive(AsStaticStr, attributes(strum))]
#[deprecated(
since = "0.22.0",
Expand Down Expand Up @@ -736,7 +767,7 @@ pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
/// // Bring trait into scope
/// use std::str::FromStr;
/// use strum::{IntoEnumIterator, EnumMessage};
/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString, EnumMessage};
/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString};
///
/// #[derive(Debug)]
/// struct NonDefault;
Expand Down
1 change: 1 addition & 0 deletions strum_macros/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod enum_properties;
pub mod enum_try_as;
pub mod enum_variant_names;
pub mod from_repr;
pub mod static_variants_array;

mod strings;

Expand Down
36 changes: 36 additions & 0 deletions strum_macros/src/macros/static_variants_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields};

use crate::helpers::{non_enum_error, HasTypeProperties, non_unit_variant_error};

pub fn static_variants_array_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 variants = match &ast.data {
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 idents = variants
.iter()
.cloned()
.map(|v| {
match v.fields {
Fields::Unit => Ok(v.ident),
_ => Err(non_unit_variant_error())
}
})
.collect::<syn::Result<Vec<_>>>()?;

Ok(quote! {
impl #impl_generics #strum_module_path::StaticVariantsArray for #name #ty_generics #where_clause {
const ALL_VARIANTS: &'static [Self] = &[ #(#name::#idents),* ];
}
})
}
87 changes: 87 additions & 0 deletions strum_tests/tests/static_variants_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use strum::{StaticVariantsArray, EnumDiscriminants};

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

#[test]
fn simple() {
#[derive(StaticVariantsArray, PartialEq, Eq, Debug)]
enum Operation {
Add,
Sub,
Mul,
Div,
}

assert_eq!(
Operation::ALL_VARIANTS,
&[
Operation::Add,
Operation::Sub,
Operation::Mul,
Operation::Div,
]
);
}

#[test]
fn in_enum_discriminants() {
#[allow(dead_code)]
#[derive(EnumDiscriminants)]
#[strum_discriminants(derive(StaticVariantsArray))]
#[strum_discriminants(name(GeometricShapeDiscriminants))]
enum GeometricShape {
Point,
Circle(i32),
Rectangle {
width: i32,
height: i32,
}
}

assert_eq!(
GeometricShapeDiscriminants::ALL_VARIANTS,
&[
GeometricShapeDiscriminants::Point,
GeometricShapeDiscriminants::Circle,
GeometricShapeDiscriminants::Rectangle,
]
);
}

#[test]
fn empty_enum() {
#[derive(StaticVariantsArray, PartialEq, Eq, Debug)]
enum Empty {}

assert_eq!(
Empty::ALL_VARIANTS,
&[],
);
}

#[test]
fn variants_with_values() {
#[derive(StaticVariantsArray, PartialEq, Eq, Debug)]
enum WeekDay {
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
}

assert_eq!(
WeekDay::ALL_VARIANTS,
&[
WeekDay::Sunday,
WeekDay::Monday,
WeekDay::Tuesday,
WeekDay::Wednesday,
WeekDay::Thursday,
WeekDay::Friday,
WeekDay::Saturday,
],
);
}

0 comments on commit 2868766

Please sign in to comment.