EnumVariantMeta derive (#1972)

There are cases where we want an enum variant name. Right now the only way to do that with rust's std is to derive Debug, but this will also print out the variant's fields. This creates the unfortunate situation where we need to manually write out each variant's string name (ex: in #1963), which is both boilerplate-ey and error-prone. Crates such as `strum` exist for this reason, but it includes a lot of code and complexity that we don't need.

This adds a dead-simple `EnumVariantMeta` derive that exposes `enum_variant_index` and `enum_variant_name` functions. This allows us to make cases like #1963 much cleaner (see the second commit). We might also be able to reuse this logic for `bevy_reflect` enum derives.
This commit is contained in:
Carter Anderson 2021-04-21 23:46:54 +00:00
parent e4e32598a9
commit 1248a639ee
8 changed files with 99 additions and 123 deletions

View File

@ -0,0 +1,40 @@
use crate::modules::{get_modules, get_path};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput};
pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let variants = match &ast.data {
Data::Enum(v) => &v.variants,
_ => panic!("Expected an enum."),
};
let modules = get_modules(&ast.attrs);
let bevy_util_path = get_path(&modules.bevy_utils);
let generics = ast.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let struct_name = &ast.ident;
let idents = variants.iter().map(|v| &v.ident);
let names = variants.iter().map(|v| v.ident.to_string());
let indices = 0..names.len();
TokenStream::from(quote! {
impl #impl_generics #bevy_util_path::EnumVariantMeta for #struct_name#ty_generics #where_clause {
fn enum_variant_index(&self) -> usize {
match self {
#(#struct_name::#idents {..} => #indices,)*
}
}
fn enum_variant_name(&self) -> &'static str {
static variants: &[&str] = &[
#(#names,)*
];
let index = self.enum_variant_index();
variants[index]
}
}
})
}

View File

@ -3,6 +3,7 @@ extern crate proc_macro;
mod app_plugin; mod app_plugin;
mod bevy_main; mod bevy_main;
mod bytes; mod bytes;
mod enum_variant_meta;
mod modules; mod modules;
mod render_resource; mod render_resource;
mod render_resources; mod render_resources;
@ -54,3 +55,8 @@ pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream { pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream {
bevy_main::bevy_main(attr, item) bevy_main::bevy_main(attr, item)
} }
#[proc_macro_derive(EnumVariantMeta, attributes(as_crate))]
pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {
enum_variant_meta::derive_enum_variant_meta(input)
}

View File

@ -7,6 +7,7 @@ pub struct Modules {
pub bevy_render: String, pub bevy_render: String,
pub bevy_asset: String, pub bevy_asset: String,
pub bevy_core: String, pub bevy_core: String,
pub bevy_utils: String,
pub bevy_app: String, pub bevy_app: String,
} }
@ -16,6 +17,7 @@ impl Modules {
bevy_asset: format!("{}::asset", name), bevy_asset: format!("{}::asset", name),
bevy_render: format!("{}::render", name), bevy_render: format!("{}::render", name),
bevy_core: format!("{}::core", name), bevy_core: format!("{}::core", name),
bevy_utils: format!("{}::utils", name),
bevy_app: format!("{}::app", name), bevy_app: format!("{}::app", name),
} }
} }
@ -25,6 +27,7 @@ impl Modules {
bevy_asset: "bevy_asset".to_string(), bevy_asset: "bevy_asset".to_string(),
bevy_render: "bevy_render".to_string(), bevy_render: "bevy_render".to_string(),
bevy_core: "bevy_core".to_string(), bevy_core: "bevy_core".to_string(),
bevy_utils: "bevy_utils".to_string(),
bevy_app: "bevy_app".to_string(), bevy_app: "bevy_app".to_string(),
} }
} }

View File

@ -15,6 +15,7 @@ use bevy_ecs::{
}; };
use bevy_math::*; use bevy_math::*;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_utils::EnumVariantMeta;
use std::{borrow::Cow, collections::BTreeMap}; use std::{borrow::Cow, collections::BTreeMap};
use crate::pipeline::{InputStepMode, VertexAttribute, VertexBufferLayout}; use crate::pipeline::{InputStepMode, VertexAttribute, VertexBufferLayout};
@ -24,7 +25,7 @@ pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
/// An array where each entry describes a property of a single vertex. /// An array where each entry describes a property of a single vertex.
#[derive(Clone, Debug)] #[derive(Clone, Debug, EnumVariantMeta)]
pub enum VertexAttributeValues { pub enum VertexAttributeValues {
Float(Vec<f32>), Float(Vec<f32>),
Int(Vec<i32>), Int(Vec<i32>),

View File

@ -26,49 +26,25 @@
//! ``` //! ```
use crate::mesh::VertexAttributeValues; use crate::mesh::VertexAttributeValues;
use std::{convert::TryFrom, error::Error, fmt}; use bevy_utils::EnumVariantMeta;
use std::convert::TryFrom;
use thiserror::Error;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Error)]
#[error("cannot convert VertexAttributeValues::{variant} to {into}")]
pub struct FromVertexAttributeError { pub struct FromVertexAttributeError {
from: VertexAttributeValues, from: VertexAttributeValues,
variant: &'static str,
into: &'static str, into: &'static str,
} }
impl Error for FromVertexAttributeError {} impl FromVertexAttributeError {
fn new<T: 'static>(from: VertexAttributeValues) -> Self {
impl fmt::Display for FromVertexAttributeError { Self {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { variant: from.enum_variant_name(),
let value_name = match self.from { into: std::any::type_name::<T>(),
VertexAttributeValues::Float(..) => "VertexAttributeValues::Float", from,
VertexAttributeValues::Int(..) => "VertexAttributeValues::Int", }
VertexAttributeValues::Uint(..) => "VertexAttributeValues::Uint",
VertexAttributeValues::Float2(..) => "VertexAttributeValues::Float2",
VertexAttributeValues::Int2(..) => "VertexAttributeValues::Int2",
VertexAttributeValues::Uint2(..) => "VertexAttributeValues::Uint2",
VertexAttributeValues::Float3(..) => "VertexAttributeValues::Float3",
VertexAttributeValues::Int3(..) => "VertexAttributeValues::Int3",
VertexAttributeValues::Uint3(..) => "VertexAttributeValues::Uint3",
VertexAttributeValues::Float4(..) => "VertexAttributeValues::Float4",
VertexAttributeValues::Int4(..) => "VertexAttributeValues::Int4",
VertexAttributeValues::Uint4(..) => "VertexAttributeValues::Uint4",
VertexAttributeValues::Short2(..) => "VertexAttributeValues::Short2",
VertexAttributeValues::Short2Norm(..) => "VertexAttributeValues::Short2Norm",
VertexAttributeValues::Ushort2(..) => "VertexAttributeValues::Ushort2",
VertexAttributeValues::Ushort2Norm(..) => "VertexAttributeValues::Ushort2Norm",
VertexAttributeValues::Short4(..) => "VertexAttributeValues::Short4",
VertexAttributeValues::Short4Norm(..) => "VertexAttributeValues::Short4Norm",
VertexAttributeValues::Ushort4(..) => "VertexAttributeValues::Ushort4",
VertexAttributeValues::Ushort4Norm(..) => "VertexAttributeValues::Ushort4Norm",
VertexAttributeValues::Char2(..) => "VertexAttributeValues::Char2",
VertexAttributeValues::Char2Norm(..) => "VertexAttributeValues::Char2Norm",
VertexAttributeValues::Uchar2(..) => "VertexAttributeValues::Uchar2",
VertexAttributeValues::Uchar2Norm(..) => "VertexAttributeValues::Uchar2Norm",
VertexAttributeValues::Char4(..) => "VertexAttributeValues::Char4",
VertexAttributeValues::Char4Norm(..) => "VertexAttributeValues::Char4Norm",
VertexAttributeValues::Uchar4(..) => "VertexAttributeValues::Uchar4",
VertexAttributeValues::Uchar4Norm(..) => "VertexAttributeValues::Uchar4Norm",
};
write!(f, "can't convert `{}` to `{}`", value_name, self.into)
} }
} }
@ -157,10 +133,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u8; 4]> {
match value { match value {
VertexAttributeValues::Uchar4(value) => Ok(value), VertexAttributeValues::Uchar4(value) => Ok(value),
VertexAttributeValues::Uchar4Norm(value) => Ok(value), VertexAttributeValues::Uchar4Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[u8; 4]>",
}),
} }
} }
} }
@ -172,10 +145,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i8; 4]> {
match value { match value {
VertexAttributeValues::Char4(value) => Ok(value), VertexAttributeValues::Char4(value) => Ok(value),
VertexAttributeValues::Char4Norm(value) => Ok(value), VertexAttributeValues::Char4Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i8; 4]>",
}),
} }
} }
} }
@ -187,10 +157,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u8; 2]> {
match value { match value {
VertexAttributeValues::Uchar2(value) => Ok(value), VertexAttributeValues::Uchar2(value) => Ok(value),
VertexAttributeValues::Uchar2Norm(value) => Ok(value), VertexAttributeValues::Uchar2Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[u8; 2]>",
}),
} }
} }
} }
@ -202,10 +169,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i8; 2]> {
match value { match value {
VertexAttributeValues::Char2(value) => Ok(value), VertexAttributeValues::Char2(value) => Ok(value),
VertexAttributeValues::Char2Norm(value) => Ok(value), VertexAttributeValues::Char2Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i8; 2]>",
}),
} }
} }
} }
@ -217,10 +181,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i16; 4]> {
match value { match value {
VertexAttributeValues::Short4(value) => Ok(value), VertexAttributeValues::Short4(value) => Ok(value),
VertexAttributeValues::Short4Norm(value) => Ok(value), VertexAttributeValues::Short4Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i16; 4]>",
}),
} }
} }
} }
@ -232,10 +193,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u16; 4]> {
match value { match value {
VertexAttributeValues::Ushort4(value) => Ok(value), VertexAttributeValues::Ushort4(value) => Ok(value),
VertexAttributeValues::Ushort4Norm(value) => Ok(value), VertexAttributeValues::Ushort4Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[u16; 4]>",
}),
} }
} }
} }
@ -247,10 +205,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u16; 2]> {
match value { match value {
VertexAttributeValues::Ushort2(value) => Ok(value), VertexAttributeValues::Ushort2(value) => Ok(value),
VertexAttributeValues::Ushort2Norm(value) => Ok(value), VertexAttributeValues::Ushort2Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i16; 2]>",
}),
} }
} }
} }
@ -262,10 +217,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i16; 2]> {
match value { match value {
VertexAttributeValues::Short2(value) => Ok(value), VertexAttributeValues::Short2(value) => Ok(value),
VertexAttributeValues::Short2Norm(value) => Ok(value), VertexAttributeValues::Short2Norm(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i16; 2]>",
}),
} }
} }
} }
@ -276,10 +228,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u32; 4]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Uint4(value) => Ok(value), VertexAttributeValues::Uint4(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[u32; 4]>",
}),
} }
} }
} }
@ -290,10 +239,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i32; 4]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Int4(value) => Ok(value), VertexAttributeValues::Int4(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i32; 4]>",
}),
} }
} }
} }
@ -304,10 +250,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[f32; 4]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Float4(value) => Ok(value), VertexAttributeValues::Float4(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[f32; 4]>",
}),
} }
} }
} }
@ -318,10 +261,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u32; 3]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Uint3(value) => Ok(value), VertexAttributeValues::Uint3(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[u32; 3]>",
}),
} }
} }
} }
@ -332,10 +272,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i32; 3]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Int3(value) => Ok(value), VertexAttributeValues::Int3(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i32; 3]>",
}),
} }
} }
} }
@ -346,10 +283,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[f32; 3]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Float3(value) => Ok(value), VertexAttributeValues::Float3(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[f32; 3]>",
}),
} }
} }
} }
@ -360,10 +294,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[u32; 2]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Uint2(value) => Ok(value), VertexAttributeValues::Uint2(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[u32; 2]>",
}),
} }
} }
} }
@ -374,10 +305,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[i32; 2]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Int2(value) => Ok(value), VertexAttributeValues::Int2(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[i32; 2]>",
}),
} }
} }
} }
@ -388,10 +316,7 @@ impl TryFrom<VertexAttributeValues> for Vec<[f32; 2]> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Float2(value) => Ok(value), VertexAttributeValues::Float2(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<[f32; 2]>",
}),
} }
} }
} }
@ -402,10 +327,7 @@ impl TryFrom<VertexAttributeValues> for Vec<u32> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Uint(value) => Ok(value), VertexAttributeValues::Uint(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<u32>",
}),
} }
} }
} }
@ -416,10 +338,7 @@ impl TryFrom<VertexAttributeValues> for Vec<i32> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Int(value) => Ok(value), VertexAttributeValues::Int(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<i32>",
}),
} }
} }
} }
@ -430,10 +349,7 @@ impl TryFrom<VertexAttributeValues> for Vec<f32> {
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> { fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value { match value {
VertexAttributeValues::Float(value) => Ok(value), VertexAttributeValues::Float(value) => Ok(value),
_ => Err(FromVertexAttributeError { _ => Err(FromVertexAttributeError::new::<Self>(value)),
from: value,
into: "Vec<f32>",
}),
} }
} }
} }
@ -442,7 +358,6 @@ impl TryFrom<VertexAttributeValues> for Vec<f32> {
mod tests { mod tests {
use super::VertexAttributeValues; use super::VertexAttributeValues;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
#[test] #[test]
fn f32() { fn f32() {
let buffer = vec![0.0; 10]; let buffer = vec![0.0; 10];
@ -598,9 +513,9 @@ mod tests {
}; };
assert_eq!( assert_eq!(
format!("{}", error), format!("{}", error),
"can't convert `VertexAttributeValues::Uint4` to `Vec<u32>`" "cannot convert VertexAttributeValues::Uint4 to alloc::vec::Vec<u32>"
); );
assert_eq!(format!("{:?}", error), assert_eq!(format!("{:?}", error),
"FromVertexAttributeError { from: Uint4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), into: \"Vec<u32>\" }"); "FromVertexAttributeError { from: Uint4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), variant: \"Uint4\", into: \"alloc::vec::Vec<u32>\" }");
} }
} }

View File

@ -13,6 +13,7 @@ license = "MIT"
keywords = ["bevy"] keywords = ["bevy"]
[dependencies] [dependencies]
bevy_derive = { path = "../bevy_derive", version = "0.5.0" }
ahash = "0.7.0" ahash = "0.7.0"
tracing = {version = "0.1", features = ["release_max_level_info"]} tracing = {version = "0.1", features = ["release_max_level_info"]}
instant = { version = "0.1", features = ["wasm-bindgen"] } instant = { version = "0.1", features = ["wasm-bindgen"] }

View File

@ -0,0 +1,6 @@
pub use bevy_derive::EnumVariantMeta;
pub trait EnumVariantMeta {
fn enum_variant_index(&self) -> usize;
fn enum_variant_name(&self) -> &'static str;
}

View File

@ -1,10 +1,14 @@
mod enum_variant_meta;
pub use enum_variant_meta::*;
pub use ahash::AHasher; pub use ahash::AHasher;
use ahash::RandomState;
pub use instant::{Duration, Instant}; pub use instant::{Duration, Instant};
use std::{future::Future, pin::Pin};
pub use tracing; pub use tracing;
pub use uuid::Uuid; pub use uuid::Uuid;
use ahash::RandomState;
use std::{future::Future, pin::Pin};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;