diff options
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | ecs/Cargo.toml | 2 | ||||
-rw-r--r-- | ecs/build.rs | 98 | ||||
-rw-r--r-- | ecs/src/lib.rs | 4 | ||||
-rw-r--r-- | ecs/src/system.rs | 83 | ||||
-rw-r--r-- | ecs/src/system/stateful.rs | 31 | ||||
-rw-r--r-- | ecs/src/tuple.rs | 137 |
7 files changed, 180 insertions, 191 deletions
@@ -124,9 +124,11 @@ version = "0.1.0" dependencies = [ "ecs-macros", "itertools", + "paste", "proc-macro2", "quote", "seq-macro", + "util-macros", ] [[package]] @@ -369,6 +371,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -584,6 +592,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] +name = "util-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index ba31a1d..618dd48 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -5,7 +5,9 @@ edition = "2021" [dependencies] seq-macro = "0.3.5" +paste = "1.0.14" ecs-macros = { path = "../ecs-macros" } +util-macros = { path = "../util-macros" } [build-dependencies] quote = "1.0.35" diff --git a/ecs/build.rs b/ecs/build.rs deleted file mode 100644 index 1fbde2a..0000000 --- a/ecs/build.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::collections::HashSet; -use std::path::PathBuf; - -use itertools::Itertools; -use proc_macro2::{Delimiter, Group, Ident}; -use quote::{format_ident, quote, ToTokens}; - -const CNT: usize = 4; - -fn main() -{ - let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - - let impls = (0..CNT).flat_map(create_input_filter_impls).join("\n"); - - std::fs::write(out_dir.join("system_input_impls.rs"), impls).unwrap(); -} - -fn create_input_filter_impls(cnt: usize) -> Vec<proc_macro2::TokenStream> -{ - let mut present = HashSet::new(); - - let elements = (0..cnt) - .map(|_| ElementKind::Element) - .chain(vec![ElementKind::Excluded; cnt]) - .permutations(cnt) - .filter(|combination| { - if present.contains(combination) { - return false; - } - - present.insert(combination.clone()); - - true - }) - .map(|elements| { - elements - .into_iter() - .enumerate() - .map(|(index, element)| match element { - ElementKind::Element => { - IdentOrTuple::Ident(format_ident!("Elem{index}")) - } - ElementKind::Excluded => IdentOrTuple::Tuple, - }) - .collect::<Vec<_>>() - }) - .collect::<Vec<_>>(); - - elements - .into_iter() - .map(create_single_input_filter_impl) - .collect() -} - -fn create_single_input_filter_impl( - elements: Vec<IdentOrTuple>, -) -> proc_macro2::TokenStream -{ - let ident_elements = elements - .iter() - .filter(|element| matches!(element, IdentOrTuple::Ident(_))) - .collect::<Vec<_>>(); - - quote! { - impl<#(#ident_elements: Input,)*> InputFilter for (#(#elements,)*) { - type Filtered = (#(#ident_elements,)*); - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -enum ElementKind -{ - Element, - Excluded, -} - -#[derive(Debug)] -enum IdentOrTuple -{ - Ident(Ident), - Tuple, -} - -impl ToTokens for IdentOrTuple -{ - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) - { - match self { - Self::Ident(ident) => ident.to_tokens(tokens), - Self::Tuple => { - Group::new(Delimiter::Parenthesis, proc_macro2::TokenStream::new()) - .to_tokens(tokens) - } - } - } -} diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 573aa41..4e3e4a1 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -14,9 +14,11 @@ use crate::system::{ System, TypeErased as TypeErasedSystem, }; +use crate::tuple::FilterExclude as TupleFilterExclude; pub mod component; pub mod system; +pub mod tuple; pub use ecs_macros::Component; @@ -154,7 +156,7 @@ where Comps: ComponentSequence, { type Flags = NoInitSystemParamFlag; - type Input = (); + type Input = TupleFilterExclude; fn initialize<SystemImpl>(_system: &mut impl System<SystemImpl>, _input: Self::Input) { diff --git a/ecs/src/system.rs b/ecs/src/system.rs index d90e0a2..cbf004f 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,13 +1,13 @@ -use std::any::{Any, TypeId}; +use std::any::Any; use std::convert::Infallible; use std::fmt::Debug; -use std::mem::{transmute_copy, ManuallyDrop}; use std::ptr::addr_of_mut; use seq_macro::seq; use crate::component::Component; use crate::system::util::check_params_are_compatible; +use crate::tuple::{FilterElement as TupleFilterElement, With as TupleWith}; use crate::ComponentStorage; pub mod stateful; @@ -166,80 +166,9 @@ pub struct NoInitParamFlag {} /// A type which can be used as input to a [`System`]. pub trait Input: 'static {} -pub trait InputFilter +impl<InputT: Input, Tup> TupleFilterElement<Tup> for InputT +where + Tup: TupleWith<Self>, { - type Filtered: FilteredInputs; + type Tuple = Tup::With; } - -pub trait FilteredInputs -{ - type InOptions: OptionInputs; - - fn into_in_options(self) -> Self::InOptions; -} - -macro_rules! impl_filtered_inputs { - ($cnt: tt) => { - seq!(I in 0..$cnt { - impl<#(Input~I: Input,)*> FilteredInputs for (#(Input~I,)*) { - type InOptions = (#(Option<Input~I>,)*); - - fn into_in_options(self) -> Self::InOptions { - #![allow(clippy::unused_unit)] - (#(Some(self.I),)*) - } - } - }); - }; -} - -seq!(N in 0..4 { - impl_filtered_inputs!(N); -}); - -pub trait OptionInputs -{ - fn take<Input: 'static>(&mut self) -> TakeOptionInputResult<Input>; -} - -macro_rules! impl_option_inputs { - ($cnt: tt) => { - seq!(I in 0..$cnt { - impl<#(Input~I: 'static,)*> OptionInputs for (#(Option<Input~I>,)*) { - fn take<Input: 'static>(&mut self) -> TakeOptionInputResult<Input> { - #( - if TypeId::of::<Input~I>() == TypeId::of::<Input>() { - let input = match self.I.take() { - Some(input) => ManuallyDrop::new(input), - None => { - return TakeOptionInputResult::AlreadyTaken; - } - }; - - return TakeOptionInputResult::Found( - // SAFETY: It can be transmuted safely since it is the - // same type and the type is 'static - unsafe { transmute_copy(&input) } - ); - } - )* - - TakeOptionInputResult::NotFound - } - } - }); - }; -} - -seq!(N in 0..4 { - impl_option_inputs!(N); -}); - -pub enum TakeOptionInputResult<Input> -{ - Found(Input), - NotFound, - AlreadyTaken, -} - -include!(concat!(env!("OUT_DIR"), "/system_input_impls.rs")); diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 9b2f279..54f9807 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -6,15 +6,13 @@ use seq_macro::seq; use crate::component::Component; use crate::system::util::check_params_are_compatible; -use crate::system::{ - FilteredInputs, - InputFilter, - Into as IntoSystem, - OptionInputs, - Param, - System, - TakeOptionInputResult, - TypeErased, +use crate::system::{Into as IntoSystem, Param, System, TypeErased}; +use crate::tuple::{ + Filter as TupleFilter, + FilterExclude as TupleFilterExclude, + IntoInOptions as TupleIntoInOptions, + TakeOptionElementResult as TupleTakeOptionElementResult, + WithOptionElements as TupleWithOptionElements, }; use crate::ComponentStorage; @@ -34,25 +32,28 @@ macro_rules! impl_system { Func: Fn(#(TParam~I,)*) + Copy + 'static, #(TParam~I: Param<'world>,)* #(TParam~I::Input: 'static,)* - (#(TParam~I::Input,)*): InputFilter + (#(TParam~I::Input,)*): TupleFilter, + <(#(TParam~I::Input,)*) as TupleFilter>::Out: TupleIntoInOptions { - type Input = <(#(TParam~I::Input,)*) as InputFilter>::Filtered; + type Input = <(#(TParam~I::Input,)*) as TupleFilter>::Out; fn initialize(mut self, input: Self::Input) -> Self { let mut option_input = input.into_in_options(); #( - if TypeId::of::<TParam~I::Input>() != TypeId::of::<()>() { + if TypeId::of::<TParam~I::Input>() != + TypeId::of::<TupleFilterExclude>() + { let input = match option_input.take::<TParam~I::Input>() { - TakeOptionInputResult::Found(input) => input, - TakeOptionInputResult::NotFound => { + TupleTakeOptionElementResult::Found(input) => input, + TupleTakeOptionElementResult::NotFound => { panic!( "Parameter input {} not found", type_name::<TParam~I::Input>() ); } - TakeOptionInputResult::AlreadyTaken => { + TupleTakeOptionElementResult::AlreadyTaken => { panic!( concat!( "Parameter {} is already initialized. ", diff --git a/ecs/src/tuple.rs b/ecs/src/tuple.rs new file mode 100644 index 0000000..41ca4b3 --- /dev/null +++ b/ecs/src/tuple.rs @@ -0,0 +1,137 @@ +use std::any::TypeId; +use std::mem::{transmute_copy, ManuallyDrop}; + +use paste::paste; +use seq_macro::seq; +use util_macros::sub; + +/// Used to append a element to a tuple type. +pub trait With<OtherElem> +{ + type With; +} + +/// Used to make all elements of a tuple type wrapped in [`Option`]. +pub trait IntoInOptions +{ + type InOptions: WithOptionElements; + + fn into_in_options(self) -> Self::InOptions; +} + +/// A tuple with all elements wrapped in [`Option`]. +pub trait WithOptionElements +{ + fn take<Element: 'static>(&mut self) -> TakeOptionElementResult<Element>; +} + +/// Used to filter the elements of a tuple type. +pub trait Filter +{ + type Out; +} + +/// Used by implementations of [`Filter`] to know whether this element should be +/// filtered out or not. +pub trait FilterElement<Tup> +{ + type Tuple; +} + +/// The result of trying to [`take`] a element from a implementation of +/// [`WithOptionElements`]. +/// +/// [`take`]: WithOptionElements::take +pub enum TakeOptionElementResult<Element> +{ + /// The element was succesfully taken. + Found(Element), + + /// The element is not a element of the tuple. + NotFound, + + /// The element has already been taken + AlreadyTaken, +} + +/// Zero-sized struct excluded when filtering the elements of a tuple type using +/// implementations of [`Filter`]. +#[derive(Debug, Clone, Copy)] +pub struct FilterExclude; + +impl<Tup> FilterElement<Tup> for FilterExclude +{ + type Tuple = Tup; +} + +macro_rules! tuple_filter_elem_tuple { + (overflow) => { + () + }; + + ($index: tt) => { + paste! { + [<Elem $index>]::Tuple + } + }; +} + +macro_rules! impl_tuple_traits { + ($cnt: tt) => { + seq!(I in 0..$cnt { + impl<OtherElem, #(Elem~I,)*> With<OtherElem> for (#(Elem~I,)*) { + type With = (#(Elem~I,)* OtherElem,); + } + + impl<#(Element~I: 'static,)*> IntoInOptions for (#(Element~I,)*) + { + type InOptions = (#(Option<Element~I>,)*); + + fn into_in_options(self) -> Self::InOptions + { + #![allow(clippy::unused_unit)] + (#(Some(self.I),)*) + } + } + + impl<#(Element~I: 'static,)*> WithOptionElements for (#(Option<Element~I>,)*) + { + fn take<Element: 'static>(&mut self) + -> TakeOptionElementResult<Element> + { + #( + if TypeId::of::<Element~I>() == TypeId::of::<Element>() { + let input = match self.I.take() { + Some(input) => ManuallyDrop::new(input), + None => { + return TakeOptionElementResult::AlreadyTaken; + } + }; + + return TakeOptionElementResult::Found( + // SAFETY: It can be transmuted safely since it is the + // same type and the type is 'static + unsafe { transmute_copy(&input) } + ); + } + )* + + TakeOptionElementResult::NotFound + } + } + + paste! { + impl<#(Elem~I,)*> Filter for (#(Elem~I,)*) + where + #(Elem~I: FilterElement<sub!(I - 1, tuple_filter_elem_tuple)>,)* + { + type Out = sub!($cnt - 1, tuple_filter_elem_tuple); + } + } + }); + }; +} + +seq!(N in 0..4 { + impl_tuple_traits!(N); +}); |