summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: b7b58bac54672c9b298ce07bbbfac30fc34039ee (plain)
use varihappy_macros::{
	repeat, tuple_expr_reverse, tuple_len, tuple_new, tuple_reverse, tuple_trait_def,
	type_to_field_access,
};

pub trait Tuple {
	const LEN: usize;

	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;
}

pub trait ArrayTupleOrEmpty<T>: Tuple {}

pub trait ArrayTuple: Tuple {
	type Item;
}

impl<T> ArrayTupleOrEmpty<T> for () {}
impl<I, T: ArrayTuple<Item = I>> ArrayTupleOrEmpty<T> for T {}

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

	type Reversed = ();

	const LEN: usize = 0;

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

macro_rules! tuple_apply {
	(($($letter: ident,)*), $other: ident) => {
		$other!(($($letter,)*))
	};
}

macro_rules! tuple_rest {
	((A, $($letter: ident,)*)) => {
		($($letter,)*)
	};
}

macro_rules! tuple_expr_apply {
	($this: ident, ($($letter: ident,)*), $other: ident) => {
		$other!($this, ($($letter,)*))
	};
}

macro_rules! tuple_expr_ref {
	($this: ident, ($($letter: ident,)*)) => {
		{
			let this = $this;
			($(&type_to_field_access!($letter),)*)
		}
	}
}

macro_rules! tuple_expr_mut {
	($this: ident, ($($letter: ident,)*)) => {
		{
			let this = $this;
			($(&mut type_to_field_access!($letter),)*)
		}
	}
}

macro_rules! tuple_expr_rest {
	($this: ident, (A, $($letter: ident,)*)) => {
		#[allow(unused_variables, clippy::unused_unit)]
		{
			let this = $this;
			($(type_to_field_access!($letter),)*)
		}
	}
}

macro_rules! tuple_expr_split {
	($this: ident, (A, $($letter: ident,)*)) => {
		{
			let this = $this;
			(this.0, ($(type_to_field_access!($letter),)*))
		}
	}
}

macro_rules! impl_tuple_inner {
	(($($letter: ident,)*)) => {
		impl<$($letter),*> Tuple for ($($letter,)*) {
			const LEN: usize = tuple_len!(($($letter,)*));

			type AsRef<'a> = ($(&'a $letter,)*) where Self: 'a;
			type AsMut<'a> = ($(&'a mut $letter,)*) where Self: 'a;
			type Reversed =  tuple_apply!(($($letter,)*), tuple_reverse);

			fn as_ref(&self) -> Self::AsRef<'_> {
				tuple_expr_apply!(self, ($($letter,)*), tuple_expr_ref)
			}

			fn as_mut(&mut self) -> Self::AsMut<'_> {
				tuple_expr_apply!(self, ($($letter,)*), tuple_expr_mut)
			}

			fn rev(self) -> Self::Reversed {
				tuple_expr_apply!(self, ($($letter,)*), tuple_expr_reverse)
			}
		}

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

			fn into_0(self) -> Self::Item0 {
				self.0
			}
			fn split_into(self) -> (Self::Item0, Self::Rest) {
				tuple_expr_apply!(self, ($($letter,)*), tuple_expr_split)
			}

			fn into_rest(self) -> Self::Rest {
				tuple_expr_apply!(self, ($($letter,)*), tuple_expr_rest)
			}
		}
	};
}

macro_rules! impl_tuple {
	($number: literal) => {
		tuple_new! { $number, impl_tuple_inner }
	};
}

macro_rules! tuple_trait_inner {
	(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()
			}
		}
	};
}

macro_rules! tuple_trait {
	($number: literal) => {
		tuple_trait_def! { $number, tuple_trait_inner }
	};
}

macro_rules! t {
	($_t:tt) => {
		T
	};
}

macro_rules! impl_array_tuple_inner {
	(($($letter: ident,)*)) => {
		impl<T> ArrayTuple for ($(t!($letter),)*) {
			type Item = T;
		}
	};
}

macro_rules! impl_array_tuple {
	($number: literal) => {
		tuple_new! { $number, impl_array_tuple_inner }
	};
}

repeat!(1..64, impl_tuple);
repeat!(2..64, tuple_trait);
repeat!(1..64, impl_array_tuple);