From 8022e8998290b067b8aa0cb9cba8ba410826bdab Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 21 May 2026 17:55:20 +0200 Subject: chore: rename ecs* crates to engine-ecs* --- engine-ecs/src/tuple.rs | 238 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 engine-ecs/src/tuple.rs (limited to 'engine-ecs/src/tuple.rs') 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 = (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: 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(&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 +{ + type Out; +} + +pub trait ReduceElement +{ + type Return; +} + +macro_rules! tuple_reduce_elem_tuple { + (overflow) => { + () + }; + + ($index: tt) => { + paste! { + []::Return + } + }; +} + +macro_rules! elem_type_by_index { + (overflow) => { + () + }; + + ($index: tt) => { + paste! { + [] + } + }; +} + +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 = (#(Elem~I,)* NewElem,); + + type WithoutLastElement = all_except_last!(start #(Elem~I)*); + + type LastElement = sub!($cnt - 1, elem_type_by_index); + + type InOptions = (#(Option,)*); + + 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(&mut self, index: usize) -> Option<&mut Element> + { + match index { + #( + I => { + assert!(TypeId::of::() == TypeId::of::()); + + // SAFETY: It is checked above that the type is correct + Some(unsafe { &mut *(&raw mut self.I).cast::() }) + } + )* + _ => None + } + } + } + + impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*) + { + } + + paste! { + impl Reduce 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 = (); + + type WithoutLastElement = all_except_last!(start #(Elem~I)*); + + type LastElement = Elem15; + + type InOptions = (#(Option,)*); + + 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(&mut self, index: usize) -> Option<&mut Element> + { + match index { + #( + I => { + assert!(TypeId::of::() == TypeId::of::()); + + // SAFETY: It is checked above that the type is correct + Some(unsafe { &mut *(&raw mut self.I).cast::() }) + } + )* + _ => None + } + } + } + + impl<#(Elem~I,)*> sealed::Sealed for (#(Elem~I,)*) + { + } +}); + +mod sealed +{ + pub trait Sealed {} +} -- cgit v1.2.3-18-g5258