summaryrefslogtreecommitdiff
path: root/ecs/src/tuple.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ecs/src/tuple.rs')
-rw-r--r--ecs/src/tuple.rs137
1 files changed, 137 insertions, 0 deletions
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);
+});