From 1291879ccaf4d17be0b99403856b20e1f4ba179c Mon Sep 17 00:00:00 2001 From: Vincent Junge Date: Tue, 7 Feb 2023 03:34:40 +0100 Subject: [PATCH] States derive macro --- crates/bevy_ecs/macros/src/lib.rs | 6 +++++ crates/bevy_ecs/macros/src/states.rs | 38 +++++++++++++++++++++++++++ crates/bevy_ecs/src/lib.rs | 1 + crates/bevy_ecs/src/schedule/state.rs | 10 +------ examples/ecs/state.rs | 10 +------ examples/games/alien_cake_addict.rs | 10 +------ examples/games/game_menu.rs | 27 ++----------------- 7 files changed, 50 insertions(+), 52 deletions(-) create mode 100644 crates/bevy_ecs/macros/src/states.rs diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 2d140f7b1a697d..7196e8c9d255cc 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -3,6 +3,7 @@ extern crate proc_macro; mod component; mod fetch; mod set; +mod states; use crate::{fetch::derive_world_query_impl, set::derive_set}; use bevy_macro_utils::{derive_boxed_label, get_named_struct_fields, BevyManifest}; @@ -558,3 +559,8 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { pub fn derive_component(input: TokenStream) -> TokenStream { component::derive_component(input) } + +#[proc_macro_derive(States)] +pub fn derive_states(input: TokenStream) -> TokenStream { + states::derive_states(input) +} diff --git a/crates/bevy_ecs/macros/src/states.rs b/crates/bevy_ecs/macros/src/states.rs new file mode 100644 index 00000000000000..f57c26154d05b1 --- /dev/null +++ b/crates/bevy_ecs/macros/src/states.rs @@ -0,0 +1,38 @@ +use proc_macro::{Span, TokenStream}; +use quote::quote; +use syn::{parse_macro_input, Data::Enum, DeriveInput}; + +pub fn derive_states(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let error = || { + syn::Error::new( + Span::call_site().into(), + "derive(States) only supports fieldless enums", + ) + .into_compile_error() + .into() + }; + let Enum(enumeration) = ast.data else { + return error(); + }; + if enumeration.variants.iter().any(|v| !v.fields.is_empty()) { + return error(); + } + + let generics = ast.generics; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let struct_name = &ast.ident; + let idents = enumeration.variants.iter().map(|v| &v.ident); + let len = idents.len(); + + quote! { + impl #impl_generics bevy::ecs::schedule_v3::States for #struct_name #ty_generics #where_clause { + type Iter = std::array::IntoIter; + + fn variants() -> Self::Iter { + [#(Self::#idents,)*].into_iter() + } + } + }.into() +} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 77d6befc5d68d4..02c935fa7ab4ff 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -48,6 +48,7 @@ pub mod prelude { }, world::{FromWorld, World}, }; + pub use bevy_ecs_macros::States; } pub use bevy_ecs_macros::all_tuples; diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 975ae9d7c1c3aa..099e6942c3e8ef 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -25,7 +25,7 @@ use crate::world::World; /// ```rust /// use bevy_ecs::prelude::States; /// -/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)] +/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] /// enum GameState { /// #[default] /// MainMenu, @@ -33,14 +33,6 @@ use crate::world::World; /// InGame, /// } /// -/// impl States for GameState { -/// type Iter = std::array::IntoIter; -/// -/// fn variants() -> Self::Iter { -/// [GameState::MainMenu, GameState::SettingsMenu, GameState::InGame].into_iter() -/// } -/// } -/// /// ``` pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default { type Iter: Iterator; diff --git a/examples/ecs/state.rs b/examples/ecs/state.rs index 0f50d679636adc..4404e387294ea8 100644 --- a/examples/ecs/state.rs +++ b/examples/ecs/state.rs @@ -25,21 +25,13 @@ fn main() { .run(); } -#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash, States)] enum AppState { #[default] Menu, InGame, } -impl States for AppState { - type Iter = std::array::IntoIter; - - fn variants() -> Self::Iter { - [AppState::Menu, AppState::InGame].into_iter() - } -} - #[derive(Resource)] struct MenuData { button_entity: Entity, diff --git a/examples/games/alien_cake_addict.rs b/examples/games/alien_cake_addict.rs index 0b1a14455c1142..de8fc73c331d30 100644 --- a/examples/games/alien_cake_addict.rs +++ b/examples/games/alien_cake_addict.rs @@ -5,21 +5,13 @@ use std::f32::consts::PI; use bevy::prelude::*; use rand::Rng; -#[derive(Clone, Eq, PartialEq, Debug, Hash, Default)] +#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)] enum GameState { #[default] Playing, GameOver, } -impl States for GameState { - type Iter = std::array::IntoIter; - - fn variants() -> Self::Iter { - [GameState::Playing, GameState::GameOver].into_iter() - } -} - #[derive(Resource)] struct BonusSpawnTimer(Timer); diff --git a/examples/games/game_menu.rs b/examples/games/game_menu.rs index 46433e4c42abc8..b7b4ae617ca578 100644 --- a/examples/games/game_menu.rs +++ b/examples/games/game_menu.rs @@ -7,7 +7,7 @@ use bevy::prelude::*; const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); // Enum that will be used as a global state for the game -#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)] +#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] enum GameState { #[default] Splash, @@ -15,14 +15,6 @@ enum GameState { Game, } -impl States for GameState { - type Iter = std::array::IntoIter; - - fn variants() -> Self::Iter { - [GameState::Splash, GameState::Menu, GameState::Game].into_iter() - } -} - // One of the two settings that can be set through the menu. It will be a resource in the app #[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] enum DisplayQuality { @@ -312,7 +304,7 @@ mod menu { } // State used for the current menu screen - #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash)] + #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] enum MenuState { Main, Settings, @@ -322,21 +314,6 @@ mod menu { Disabled, } - impl States for MenuState { - type Iter = std::array::IntoIter; - - fn variants() -> Self::Iter { - [ - MenuState::Main, - MenuState::Settings, - MenuState::SettingsDisplay, - MenuState::SettingsSound, - MenuState::Disabled, - ] - .into_iter() - } - } - // Tag component used to tag entities added on the main menu screen #[derive(Component)] struct OnMainMenuScreen;