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 remove the last element of a tuple type. /// /// For example: `(u32, String, PathBuf)` becomes `(u32, String)`. pub trait WithoutLast { type Return; } /// 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, } 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 }; } pub trait Pop: WithoutLast { type LastElem; fn pop(self) -> (::Return, Self::LastElem); } 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 With for (#(Elem~I,)*) { type With = (#(Elem~I,)* OtherElem,); } impl<#(Elem~I,)*> WithoutLast for (#(Elem~I,)*) { type Return = all_except_last!(start #(Elem~I)*); } 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 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); } } impl<#(Elem~I,)*> Pop for (#(Elem~I,)*) { type LastElem = sub!($cnt - 1, elem_type_by_index); fn pop(self) -> (::Return, Self::LastElem) { ( all_except_last!(start #((self.I))*), sub!($cnt - 1, elem_by_index, (self)) ) } } }); }; } seq!(N in 0..16 { impl_tuple_traits!(N); });