summaryrefslogtreecommitdiff
path: root/engine-ecs/src/tuple.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-05-21 17:55:20 +0200
committerHampusM <hampus@hampusmat.com>2026-05-21 17:55:20 +0200
commit8022e8998290b067b8aa0cb9cba8ba410826bdab (patch)
tree7171e79ce530e03079046ee8fd12167160c45480 /engine-ecs/src/tuple.rs
parent412cee02c252f91bcf0b70a3f5cc5fca6d2b4c62 (diff)
chore: rename ecs* crates to engine-ecs*HEADmaster
Diffstat (limited to 'engine-ecs/src/tuple.rs')
-rw-r--r--engine-ecs/src/tuple.rs238
1 files changed, 238 insertions, 0 deletions
diff --git a/engine-ecs/src/tuple.rs b/engine-ecs/src/tuple.rs
new file mode 100644
index 0000000..def25a0
--- /dev/null
+++ b/engine-ecs/src/tuple.rs
@@ -0,0 +1,238 @@
+use std::any::TypeId;
+
+use paste::paste;
+use seq_macro::seq;
+use util_macros::sub;
+
+pub trait Tuple: sealed::Sealed
+{
+ /// `Self` with the given type added as the last element.
+ ///
+ /// `(String, i32, u8)::WithElementAtEnd<Path> = (String, i32, u8, Path)`
+ ///
+ /// # Important note
+ /// If `Self` has 16 elements, this will be `()`. The reason for this is that the
+ /// `Tuple` trait is only implemented for tuples with up to and including 16 elements.
+ type WithElementAtEnd<NewElem>: Tuple;
+
+ /// `Self` without the last element.
+ ///
+ /// `(u16, AtomicU8)::WithoutLastElement = (u16,)`
+ type WithoutLastElement: Tuple;
+
+ /// The last element of `Self`.
+ type LastElement;
+
+ /// Self with all elements wrapped in [`Option`].
+ type InOptions: Tuple;
+
+ /// Pops the last element from this tuple, returning the new tuple and the popped
+ /// element.
+ fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement);
+
+ /// Converts this tuple so that all elements are wrapped in [`Option`].
+ fn into_in_options(self) -> Self::InOptions;
+}
+
+/// A tuple with element types that all have the lifetime `'static`.
+pub trait WithAllElemLtStatic: Tuple + sealed::Sealed
+{
+ /// Returns the element at the given index.
+ fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element>;
+}
+
+/// Using the type system, reduces the elements of a tuple to a single one. Each element
+/// determines itself how it is handled.
+pub trait Reduce<Operation>
+{
+ type Out;
+}
+
+pub trait ReduceElement<Accumulator, Operation>
+{
+ type Return;
+}
+
+macro_rules! tuple_reduce_elem_tuple {
+ (overflow) => {
+ ()
+ };
+
+ ($index: tt) => {
+ paste! {
+ [<Elem $index>]::Return
+ }
+ };
+}
+
+macro_rules! elem_type_by_index {
+ (overflow) => {
+ ()
+ };
+
+ ($index: tt) => {
+ paste! {
+ [<Elem $index>]
+ }
+ };
+}
+
+macro_rules! elem_by_index {
+ (overflow) => {
+ ()
+ };
+
+ ($index: tt, $self: ident) => {
+ $self.$index
+ };
+}
+
+macro_rules! all_except_last {
+ (start $($rest: tt)*) => {
+ all_except_last!(@[] $($rest)*)
+ };
+
+ (@[$($included_elem: ident,)*] $elem: ident $($rest: tt)+) => {
+ all_except_last!(@[$($included_elem,)* $elem,] $($rest)*)
+ };
+
+ (@[$($included_elem: expr,)*] ($elem: expr) $($rest: tt)+) => {
+ all_except_last!(@[$($included_elem,)* $elem,] $($rest)*)
+ };
+
+ (@[$($included_elem: ident,)*] $elem: ident) => {
+ ($($included_elem,)*)
+ };
+
+ (@[$($included_elem: expr,)*] $elem: expr) => {
+ ($($included_elem,)*)
+ };
+
+ (@[]) => {
+ ()
+ };
+}
+
+macro_rules! impl_tuple_traits {
+ ($cnt: tt) => {
+ seq!(I in 0..$cnt {
+ impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*)
+ {
+ type WithElementAtEnd<NewElem> = (#(Elem~I,)* NewElem,);
+
+ type WithoutLastElement = all_except_last!(start #(Elem~I)*);
+
+ type LastElement = sub!($cnt - 1, elem_type_by_index);
+
+ type InOptions = (#(Option<Elem~I>,)*);
+
+ fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement)
+ {
+ (
+ all_except_last!(start #((self.I))*),
+ sub!($cnt - 1, elem_by_index, (self))
+ )
+ }
+
+ fn into_in_options(self) -> Self::InOptions
+ {
+ #![allow(clippy::unused_unit)]
+ (#(Some(self.I),)*)
+ }
+ }
+
+ impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*)
+ {
+ fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element>
+ {
+ match index {
+ #(
+ I => {
+ assert!(TypeId::of::<Element>() == TypeId::of::<Elem~I>());
+
+ // SAFETY: It is checked above that the type is correct
+ Some(unsafe { &mut *(&raw mut self.I).cast::<Element>() })
+ }
+ )*
+ _ => None
+ }
+ }
+ }
+
+ impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*)
+ {
+ }
+
+ paste! {
+ impl<Operation, #(Elem~I,)*> Reduce<Operation> for (#(Elem~I,)*)
+ where
+ #(
+ Elem~I: ReduceElement<
+ sub!(I - 1, tuple_reduce_elem_tuple), Operation
+ >,
+ )*
+ {
+ type Out = sub!($cnt - 1, tuple_reduce_elem_tuple);
+ }
+ }
+ });
+ };
+}
+
+seq!(N in 0..16 {
+ impl_tuple_traits!(N);
+});
+
+seq!(I in 0..16 {
+ impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*)
+ {
+ type WithElementAtEnd<NewElem> = ();
+
+ type WithoutLastElement = all_except_last!(start #(Elem~I)*);
+
+ type LastElement = Elem15;
+
+ type InOptions = (#(Option<Elem~I>,)*);
+
+ fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement)
+ {
+ (
+ all_except_last!(start #((self.I))*),
+ self.15
+ )
+ }
+
+ fn into_in_options(self) -> Self::InOptions
+ {
+ #![allow(clippy::unused_unit)]
+ (#(Some(self.I),)*)
+ }
+ }
+
+ impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*)
+ {
+ fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element>
+ {
+ match index {
+ #(
+ I => {
+ assert!(TypeId::of::<Element>() == TypeId::of::<Elem~I>());
+
+ // SAFETY: It is checked above that the type is correct
+ Some(unsafe { &mut *(&raw mut self.I).cast::<Element>() })
+ }
+ )*
+ _ => None
+ }
+ }
+ }
+
+ impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*)
+ {
+ }
+});
+
+mod sealed
+{
+ pub trait Sealed {}
+}