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 { 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(&mut self) -> TakeOptionElementResult; } /// 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; } /// The result of trying to [`take`] a element from a implementation of /// [`WithOptionElements`]. /// /// [`take`]: WithOptionElements::take pub enum TakeOptionElementResult { /// 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 implementing [`ReduceElement`] and just returns the accumulator. #[derive(Debug, Clone, Copy)] pub struct ReduceNoOp; impl ReduceElement for ReduceNoOp { type Return = Accumulator; } macro_rules! tuple_reduce_elem_tuple { (overflow) => { () }; ($index: tt) => { paste! { []::Return } }; } macro_rules! impl_tuple_traits { ($cnt: tt) => { seq!(I in 0..$cnt { impl With for (#(Elem~I,)*) { type With = (#(Elem~I,)* OtherElem,); } impl<#(Element~I: 'static,)*> IntoInOptions for (#(Element~I,)*) { type InOptions = (#(Option,)*); fn into_in_options(self) -> Self::InOptions { #![allow(clippy::unused_unit)] (#(Some(self.I),)*) } } impl<#(Element~I: 'static,)*> WithOptionElements for (#(Option,)*) { fn take(&mut self) -> TakeOptionElementResult { #( if TypeId::of::() == TypeId::of::() { 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,)*> Reduce for (#(Elem~I,)*) where #(Elem~I: ReduceElement,)* { type Out = sub!($cnt - 1, tuple_reduce_elem_tuple); } } }); }; } seq!(N in 0..16 { impl_tuple_traits!(N); });