summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 90bec23d7a1fe123ab9bc1dcdc8998552944ec05 (plain)
pub trait Tuple {
	type AsRef<'a>: Tuple
	where
		Self: 'a;
	type AsMut<'a>: Tuple
	where
		Self: 'a;

	type Reversed: Tuple;

	fn as_ref(&self) -> Self::AsRef<'_>;
	fn as_mut(&mut self) -> Self::AsMut<'_>;
	fn rev(self) -> Self::Reversed;
}

pub trait Tuple1: Tuple {
	type Item0;
	type Rest: Tuple;

	fn into_0(self) -> Self::Item0;
	fn split_into(self) -> (Self::Item0, Self::Rest);
	fn into_rest(self) -> Self::Rest;
}

macro_rules! impl_tuple {
	(
		A, $($letter: ident),* => $($letter_reverse: ident),*;
		0, $($number: tt),* => $($number_reverse: tt),*
	) => {
		impl<A,$($letter),*> Tuple for (A,$($letter,)*) {
			type AsRef<'a> = (&'a A,$(&'a $letter,)*) where Self: 'a;
			type AsMut<'a> = (&'a mut A,$(&'a mut $letter,)*) where Self: 'a;
			type Reversed =  ($($letter_reverse,)*);

			fn as_ref(&self) -> Self::AsRef<'_> {
				(&self.0,$(&self.$number,)*)
			}

			fn as_mut(&mut self) -> Self::AsMut<'_> {
				(&mut self.0,$(&mut self.$number,)*)
			}

			fn rev(self) -> Self::Reversed {
				($(self.$number_reverse,)*)
			}
		}

		impl<A, $($letter),*> Tuple1 for (A, $($letter,)*) {
			type Item0 = A;
			type Rest = ($($letter,)*);

			fn into_0(self) -> Self::Item0 {
				self.0
			}
			fn split_into(self) -> (Self::Item0, Self::Rest) {
				(self.0, ($(self.$number,)*))
			}

			fn into_rest(self) -> Self::Rest {
				($(self.$number,)*)
			}
		}
	};
}

impl Tuple for () {
	type AsRef<'a>
		= ()
	where
		Self: 'a;
	type AsMut<'a>
		= ()
	where
		Self: 'a;

	type Reversed = ();

	fn as_ref(&self) -> Self::AsRef<'_> {}
	fn as_mut(&mut self) -> Self::AsMut<'_> {}
	fn rev(self) -> Self::Reversed {}
}

impl<A> Tuple for (A,) {
	type AsRef<'a>
		= (&'a A,)
	where
		Self: 'a;
	type AsMut<'a>
		= (&'a mut A,)
	where
		Self: 'a;

	type Reversed = (A,);

	fn as_ref(&self) -> Self::AsRef<'_> {
		(&self.0,)
	}
	fn as_mut(&mut self) -> Self::AsMut<'_> {
		(&mut self.0,)
	}
	fn rev(self) -> Self::Reversed {
		self
	}
}

impl<A> Tuple1 for (A,) {
	type Item0 = A;
	type Rest = ();

	fn into_0(self) -> Self::Item0 {
		self.0
	}

	fn split_into(self) -> (Self::Item0, <Self as Tuple1>::Rest) {
		(self.0, ())
	}

	fn into_rest(self) -> Self::Rest {}
}

impl_tuple!(A, B => B, A; 0, 1 => 1, 0);
impl_tuple!(A, B, C => C, B, A; 0, 1, 2 => 2, 1, 0);
impl_tuple!(A, B, C, D => D, C, B, A; 0, 1, 2, 3 => 3, 2, 1, 0);
impl_tuple!(A, B, C, D, E => E, D, C, B, A; 0, 1, 2, 3, 4 => 4, 3, 2, 1, 0);

macro_rules! tuple_trait {
	(trait $trait_name: ident for $trait_minus1: ident {
		type $item_name: ident;
		fn $item_getter: ident;
	}) => {
		pub trait $trait_name: $trait_minus1 {
			type $item_name;
			type Rest: Tuple;

			fn $item_getter(self) -> Self::$item_name;
			fn into_rest(self) -> <Self as $trait_name>::Rest;
		}

		impl<R: Tuple1, T: $trait_minus1<Rest = R>> $trait_name for T {
			type $item_name = R::Item0;
			type Rest = R::Rest;

			fn $item_getter(self) -> Self::$item_name {
				$trait_minus1::into_rest(self).into_0()
			}

			fn into_rest(self) -> <Self as $trait_name>::Rest {
				$trait_minus1::into_rest(self).into_rest()
			}
		}
	};
}

tuple_trait!(trait Tuple2 for Tuple1 { type Item1; fn into_1; });
tuple_trait!(trait Tuple3 for Tuple2 { type Item2; fn into_2; });
tuple_trait!(trait Tuple4 for Tuple3 { type Item3; fn into_3; });
tuple_trait!(trait Tuple5 for Tuple4 { type Item4; fn into_4; });