diff --git a/Cargo.lock b/Cargo.lock index 15587c0..22a3091 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,6 +448,7 @@ version = "0.1.0" dependencies = [ "bevy-inspector-egui", "bevy_app", + "bevy_controls_macros", "bevy_derive", "bevy_ecs", "bevy_input", @@ -470,6 +471,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bevy_controls_macros" +version = "0.1.0" +dependencies = [ + "env_logger", + "quote", + "syn 1.0.109", +] + [[package]] name = "bevy_core" version = "0.12.1" diff --git a/crate/bevy_controls/Cargo.toml b/crate/bevy_controls/Cargo.toml index 3b2d2c6..dc28aa6 100644 --- a/crate/bevy_controls/Cargo.toml +++ b/crate/bevy_controls/Cargo.toml @@ -21,6 +21,7 @@ log = "0.4.20" serde = { version = "1.0.194", optional = true } strum = "0.25.0" strum_macros = "0.25.3" +bevy_controls_macros = { path = "macros", version = "0.1.0" } [dev-dependencies] env_logger = "0.10.1" diff --git a/crate/bevy_controls/macros/Cargo.toml b/crate/bevy_controls/macros/Cargo.toml new file mode 100644 index 0000000..e8ffbfd --- /dev/null +++ b/crate/bevy_controls/macros/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "bevy_controls_macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +#bevy_controls = { path = "./../bevy_controls" } +syn = { version = "1.0", features = [ "full", "extra-traits" ] } +quote = "1.0" + +[dev-dependencies] +env_logger = "0.10.1" + diff --git a/crate/bevy_controls/macros/src/lib.rs b/crate/bevy_controls/macros/src/lib.rs new file mode 100644 index 0000000..be6b253 --- /dev/null +++ b/crate/bevy_controls/macros/src/lib.rs @@ -0,0 +1,116 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use std::collections::HashMap; +//use quote::quote; +use syn::{ + parse::{Parse, ParseStream, Result}, + parse_macro_input, + punctuated::Punctuated, + token::{Bracket, Add}, + Expr, Ident, ItemTrait, Token, TypeParamBound, LitStr, +}; +use quote::quote; + +// #[cfg_feature_trait_bounds { +// "serialize" => Serialize + DeserializeOwned, +// "reflect" => Reflect + TypePath + FromReflect, +// "inspector-egui" => InspectorOptionsType, +// }] + +type RuleKey = LitStr; + +#[derive(Debug)] +struct FeatureTraitBoundMap(HashMap>); + +impl Parse for FeatureTraitBoundMap { + fn parse(input: ParseStream) -> Result { + let mut rules = HashMap::new(); + + loop { + let condition: RuleKey = input.parse()?; + + let _arrow = input.parse::]>()?; + + let supertraits = { + let mut supertraits = Punctuated::new(); + loop { + supertraits.push_value(input.parse()?); + if !input.peek(Token![+]) { + break; + } + supertraits.push_punct(input.parse()?); + + if input.peek(Token![,]) { + break; + } + } + supertraits + }; + let _comma = input.parse::(); + + rules.insert(condition, supertraits); + + if input.is_empty() { + break; + } + } + + Ok(FeatureTraitBoundMap(rules)) + } +} + +#[proc_macro_attribute] +pub fn cfg_feature_trait_bounds(attr: TokenStream, input: TokenStream) -> TokenStream { + let rules = parse_macro_input!(attr as FeatureTraitBoundMap); + let input = parse_macro_input!(input as ItemTrait); + + dbg!(std::env::vars().map(|(key, _)| key).collect::>()); + + + // let code = quote! { + // trait #name: #rules {} + // }; + // + // dbg!(code); + + TokenStream::new() +} + +#[proc_macro_attribute] +pub fn game_state_supertraits(_attrs: TokenStream, input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ItemTrait); + + let mut bounds = Vec::new(); + if cfg!(feature = "serialize") { + bounds.push(quote! { serde::Serialize }); + bounds.push(quote! { serde::de::DeserializeOwned }); + }; + if cfg!(feature = "reflect") { + bounds.push(quote! { bevy_reflect::Reflect }); + bounds.push(quote! { bevy_reflect::TypePath }); + bounds.push(quote! { bevy_reflect::FromReflect }); + }; + if cfg!(feature = "inspector-egui") { + bounds.push(quote! { bevy_inspector_egui::inspector_options::InspectorOptionsType }); + }; + + let ItemTrait { + ident, + vis, + items, + supertraits: bounds_old, + .. + } = input; + + let bounds_old = bounds_old.iter().collect::>(); + + let out = quote! { + #vis trait #ident: #(#bounds_old)+* + #(#bounds)+* { #(#items)* } + }; + + // dbg!(&out); + // dbg!(out.to_string()); + + TokenStream::from(out) +} diff --git a/crate/bevy_controls/src/contract.rs b/crate/bevy_controls/src/contract.rs index 7d6e32d..14a05c8 100644 --- a/crate/bevy_controls/src/contract.rs +++ b/crate/bevy_controls/src/contract.rs @@ -10,6 +10,7 @@ use strum::IntoEnumIterator; use bevy_reflect::{FromReflect, Reflect, TypePath}; #[cfg(feature = "serialize")] use serde::{de::DeserializeOwned, Serialize}; +use bevy_controls_macros::{cfg_feature_trait_bounds, game_state_supertraits}; use crate::resource::PlayerInputs; @@ -27,11 +28,6 @@ pub trait ActionInner: { } -pub trait GameStateInner: - 'static + std::marker::Sync + std::marker::Send + States + Default + std::fmt::Debug -{ -} - pub trait InputsContainerInner: 'static + std::marker::Sync + std::marker::Send + Resource + Default + std::fmt::Debug { @@ -91,39 +87,16 @@ pub trait Action: ActionInner + SerializeImpl + ReflectImpl + InspectorEguiImpl // ===== GameState ===== // SAFETY: inspector-egui includes reflect -#[cfg(all( - not(feature = "serialize"), - not(feature = "reflect"), - not(feature = "inspector-egui") -))] -pub trait GameState: GameStateInner {} - -#[cfg(all( - feature = "serialize", - not(feature = "reflect"), - not(feature = "inspector-egui") -))] -pub trait GameState: GameStateInner + SerializeImpl {} - -#[cfg(all( - not(feature = "serialize"), - feature = "reflect", - not(feature = "inspector-egui") -))] -pub trait GameState: GameStateInner + ReflectImpl {} - -#[cfg(all( - feature = "serialize", - feature = "reflect", - not(feature = "inspector-egui") -))] -pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl {} - -#[cfg(all(not(feature = "serialize"), feature = "inspector-egui"))] -pub trait GameState: GameStateInner + ReflectImpl + InspectorEguiImpl {} - -#[cfg(all(feature = "serialize", feature = "inspector-egui"))] -pub trait GameState: GameStateInner + SerializeImpl + ReflectImpl + InspectorEguiImpl {} +#[game_state_supertraits] +pub trait GameState: + 'static + + std::marker::Sync + + std::marker::Send + + States + + Default + + std::fmt::Debug +{ +} // ===== InputsContainer ===== // SAFETY: inspector-egui includes reflect diff --git a/crate/bevy_controls_derive/Cargo.toml b/crate/bevy_controls_derive/Cargo.toml index fe49075..b3a6b95 100644 --- a/crate/bevy_controls_derive/Cargo.toml +++ b/crate/bevy_controls_derive/Cargo.toml @@ -6,24 +6,7 @@ edition = "2021" [lib] proc-macro = true -# [features] -# default = [] -# serialize = ["bevy_input/serialize", "serde"] -# reflect = ["dep:bevy_reflect"] -# inspector-egui = ["reflect", "dep:bevy-inspector-egui"] - [dependencies] -# bevy-inspector-egui = { version = "0.22.1", optional = true } -# bevy_app = "0.12.1" -# bevy_derive = "0.12.1" -# bevy_ecs = "0.12.1" -# bevy_input = "0.12.1" -# bevy_reflect = { version = "0.12.1", optional = true } -# bevy_utils = "0.12.1" -# log = "0.4.20" -# serde = { version = "1.0.194", optional = true } -# strum = "0.25.0" -# strum_macros = "0.25.3" bevy_controls = { path = "./../bevy_controls" } syn = "1.0" quote = "1.0"