diff options
Diffstat (limited to 'ecs/src/tuple.rs')
-rw-r--r-- | ecs/src/tuple.rs | 137 |
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); +}); |