summaryrefslogtreecommitdiff
path: root/ecs
diff options
context:
space:
mode:
Diffstat (limited to 'ecs')
-rw-r--r--ecs/Cargo.toml2
-rw-r--r--ecs/build.rs98
-rw-r--r--ecs/src/lib.rs4
-rw-r--r--ecs/src/system.rs83
-rw-r--r--ecs/src/system/stateful.rs31
-rw-r--r--ecs/src/tuple.rs137
6 files changed, 164 insertions, 191 deletions
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);
+});