From 0d34b0efe3609c333a741d7c6dbfe37588bf982d Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 21 Apr 2021 23:46:54 +0000 Subject: [PATCH] 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. --- crates/bevy_derive/src/enum_variant_meta.rs | 40 +++++ crates/bevy_derive/src/lib.rs | 6 + crates/bevy_derive/src/modules.rs | 3 + crates/bevy_render/src/mesh/mesh.rs | 3 +- .../bevy_render/src/mesh/mesh/conversions.rs | 155 ++++-------------- crates/bevy_utils/Cargo.toml | 1 + crates/bevy_utils/src/enum_variant_meta.rs | 6 + crates/bevy_utils/src/lib.rs | 8 +- 8 files changed, 99 insertions(+), 123 deletions(-) create mode 100644 crates/bevy_derive/src/enum_variant_meta.rs create mode 100644 crates/bevy_utils/src/enum_variant_meta.rs diff --git a/crates/bevy_derive/src/enum_variant_meta.rs b/crates/bevy_derive/src/enum_variant_meta.rs new file mode 100644 index 00000000000000..ceb689b90bc0ff --- /dev/null +++ b/crates/bevy_derive/src/enum_variant_meta.rs @@ -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] + } + } + }) +} diff --git a/crates/bevy_derive/src/lib.rs b/crates/bevy_derive/src/lib.rs index 8603625470fa06..4b34c3ba2fdc37 100644 --- a/crates/bevy_derive/src/lib.rs +++ b/crates/bevy_derive/src/lib.rs @@ -3,6 +3,7 @@ extern crate proc_macro; mod app_plugin; mod bevy_main; mod bytes; +mod enum_variant_meta; mod modules; mod render_resource; mod render_resources; @@ -54,3 +55,8 @@ pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream { pub fn bevy_main(attr: TokenStream, item: TokenStream) -> TokenStream { 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) +} diff --git a/crates/bevy_derive/src/modules.rs b/crates/bevy_derive/src/modules.rs index 19cd16c7aaa6dd..d8014a26034f68 100644 --- a/crates/bevy_derive/src/modules.rs +++ b/crates/bevy_derive/src/modules.rs @@ -7,6 +7,7 @@ pub struct Modules { pub bevy_render: String, pub bevy_asset: String, pub bevy_core: String, + pub bevy_utils: String, pub bevy_app: String, } @@ -16,6 +17,7 @@ impl Modules { bevy_asset: format!("{}::asset", name), bevy_render: format!("{}::render", name), bevy_core: format!("{}::core", name), + bevy_utils: format!("{}::utils", name), bevy_app: format!("{}::app", name), } } @@ -25,6 +27,7 @@ impl Modules { bevy_asset: "bevy_asset".to_string(), bevy_render: "bevy_render".to_string(), bevy_core: "bevy_core".to_string(), + bevy_utils: "bevy_utils".to_string(), bevy_app: "bevy_app".to_string(), } } diff --git a/crates/bevy_render/src/mesh/mesh.rs b/crates/bevy_render/src/mesh/mesh.rs index ddb7275312282f..f4772fb4992485 100644 --- a/crates/bevy_render/src/mesh/mesh.rs +++ b/crates/bevy_render/src/mesh/mesh.rs @@ -15,6 +15,7 @@ use bevy_ecs::{ }; use bevy_math::*; use bevy_reflect::TypeUuid; +use bevy_utils::EnumVariantMeta; use std::{borrow::Cow, collections::BTreeMap}; 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; /// An array where each entry describes a property of a single vertex. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, EnumVariantMeta)] pub enum VertexAttributeValues { Float(Vec), Int(Vec), diff --git a/crates/bevy_render/src/mesh/mesh/conversions.rs b/crates/bevy_render/src/mesh/mesh/conversions.rs index 1326cd27fdfdd9..df41df5f1da2c7 100644 --- a/crates/bevy_render/src/mesh/mesh/conversions.rs +++ b/crates/bevy_render/src/mesh/mesh/conversions.rs @@ -26,49 +26,25 @@ //! ``` 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 { from: VertexAttributeValues, + variant: &'static str, into: &'static str, } -impl Error for FromVertexAttributeError {} - -impl fmt::Display for FromVertexAttributeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let value_name = match self.from { - VertexAttributeValues::Float(..) => "VertexAttributeValues::Float", - 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) +impl FromVertexAttributeError { + fn new(from: VertexAttributeValues) -> Self { + Self { + variant: from.enum_variant_name(), + into: std::any::type_name::(), + from, + } } } @@ -157,10 +133,7 @@ impl TryFrom for Vec<[u8; 4]> { match value { VertexAttributeValues::Uchar4(value) => Ok(value), VertexAttributeValues::Uchar4Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[u8; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -172,10 +145,7 @@ impl TryFrom for Vec<[i8; 4]> { match value { VertexAttributeValues::Char4(value) => Ok(value), VertexAttributeValues::Char4Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i8; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -187,10 +157,7 @@ impl TryFrom for Vec<[u8; 2]> { match value { VertexAttributeValues::Uchar2(value) => Ok(value), VertexAttributeValues::Uchar2Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[u8; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -202,10 +169,7 @@ impl TryFrom for Vec<[i8; 2]> { match value { VertexAttributeValues::Char2(value) => Ok(value), VertexAttributeValues::Char2Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i8; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -217,10 +181,7 @@ impl TryFrom for Vec<[i16; 4]> { match value { VertexAttributeValues::Short4(value) => Ok(value), VertexAttributeValues::Short4Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i16; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -232,10 +193,7 @@ impl TryFrom for Vec<[u16; 4]> { match value { VertexAttributeValues::Ushort4(value) => Ok(value), VertexAttributeValues::Ushort4Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[u16; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -247,10 +205,7 @@ impl TryFrom for Vec<[u16; 2]> { match value { VertexAttributeValues::Ushort2(value) => Ok(value), VertexAttributeValues::Ushort2Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i16; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -262,10 +217,7 @@ impl TryFrom for Vec<[i16; 2]> { match value { VertexAttributeValues::Short2(value) => Ok(value), VertexAttributeValues::Short2Norm(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i16; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -276,10 +228,7 @@ impl TryFrom for Vec<[u32; 4]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Uint4(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[u32; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -290,10 +239,7 @@ impl TryFrom for Vec<[i32; 4]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Int4(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i32; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -304,10 +250,7 @@ impl TryFrom for Vec<[f32; 4]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Float4(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[f32; 4]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -318,10 +261,7 @@ impl TryFrom for Vec<[u32; 3]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Uint3(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[u32; 3]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -332,10 +272,7 @@ impl TryFrom for Vec<[i32; 3]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Int3(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i32; 3]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -346,10 +283,7 @@ impl TryFrom for Vec<[f32; 3]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Float3(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[f32; 3]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -360,10 +294,7 @@ impl TryFrom for Vec<[u32; 2]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Uint2(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[u32; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -374,10 +305,7 @@ impl TryFrom for Vec<[i32; 2]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Int2(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[i32; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -388,10 +316,7 @@ impl TryFrom for Vec<[f32; 2]> { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Float2(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec<[f32; 2]>", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -402,10 +327,7 @@ impl TryFrom for Vec { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Uint(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -416,10 +338,7 @@ impl TryFrom for Vec { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Int(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -430,10 +349,7 @@ impl TryFrom for Vec { fn try_from(value: VertexAttributeValues) -> Result { match value { VertexAttributeValues::Float(value) => Ok(value), - _ => Err(FromVertexAttributeError { - from: value, - into: "Vec", - }), + _ => Err(FromVertexAttributeError::new::(value)), } } } @@ -442,7 +358,6 @@ impl TryFrom for Vec { mod tests { use super::VertexAttributeValues; use std::convert::{TryFrom, TryInto}; - #[test] fn f32() { let buffer = vec![0.0; 10]; @@ -598,9 +513,9 @@ mod tests { }; assert_eq!( format!("{}", error), - "can't convert `VertexAttributeValues::Uint4` to `Vec`" + "cannot convert VertexAttributeValues::Uint4 to alloc::vec::Vec" ); assert_eq!(format!("{:?}", error), - "FromVertexAttributeError { from: Uint4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), into: \"Vec\" }"); + "FromVertexAttributeError { from: Uint4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), variant: \"Uint4\", into: \"alloc::vec::Vec\" }"); } } diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index ca11ca3d868abb..c96746d01b4922 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -13,6 +13,7 @@ license = "MIT" keywords = ["bevy"] [dependencies] +bevy_derive = { path = "../bevy_derive", version = "0.5.0" } ahash = "0.7.0" tracing = {version = "0.1", features = ["release_max_level_info"]} instant = { version = "0.1", features = ["wasm-bindgen"] } diff --git a/crates/bevy_utils/src/enum_variant_meta.rs b/crates/bevy_utils/src/enum_variant_meta.rs new file mode 100644 index 00000000000000..fa55230e59af8d --- /dev/null +++ b/crates/bevy_utils/src/enum_variant_meta.rs @@ -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; +} diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 17560532b79495..9d66179df301ce 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -1,10 +1,14 @@ +mod enum_variant_meta; +pub use enum_variant_meta::*; + pub use ahash::AHasher; -use ahash::RandomState; pub use instant::{Duration, Instant}; -use std::{future::Future, pin::Pin}; pub use tracing; pub use uuid::Uuid; +use ahash::RandomState; +use std::{future::Future, pin::Pin}; + #[cfg(not(target_arch = "wasm32"))] pub type BoxedFuture<'a, T> = Pin + Send + 'a>>;