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; } /// 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 { type Tuple; } /// 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 excluded when filtering the elements of a tuple type using /// implementations of [`Filter`]. #[derive(Debug, Clone, Copy)] pub struct FilterExclude; impl FilterElement for FilterExclude { type Tuple = Tup; } macro_rules! tuple_filter_elem_tuple { (overflow) => { () }; ($index: tt) => { paste! { []::Tuple } }; } 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,)*> Filter for (#(Elem~I,)*) where #(Elem~I: FilterElement,)* { type Out = sub!($cnt - 1, tuple_filter_elem_tuple); } } }); }; } seq!(N in 0..4 { impl_tuple_traits!(N); });