use core::{
any::TypeId,
ptr::{addr_of, addr_of_mut},
};
pub use tuple_list::{tuple_list, tuple_list_type, TupleList};
use xxhash_rust::xxh3::xxh3_64;
#[rustversion::nightly]
#[must_use]
pub const fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
trait TypeEq<U: ?Sized> {
const VALUE: bool;
}
impl<T: ?Sized, U: ?Sized> TypeEq<U> for T {
default const VALUE: bool = false;
}
impl<T: ?Sized> TypeEq<T> for T {
const VALUE: bool = true;
}
<T as TypeEq<U>>::VALUE
}
#[rustversion::not(nightly)]
#[must_use]
pub const fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
true
}
pub trait HasConstLen {
const LEN: usize;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl HasConstLen for () {
const LEN: usize = 0;
fn len(&self) -> usize {
0
}
}
impl<Head, Tail> HasConstLen for (Head, Tail)
where
Tail: HasConstLen,
{
const LEN: usize = 1 + Tail::LEN;
fn len(&self) -> usize {
1 + self.1.len()
}
}
pub trait HasNameId {
fn const_name(&self) -> &'static str;
fn name_id(&self) -> u64 {
xxh3_64(self.const_name().as_bytes())
}
}
pub trait HasNameIdTuple: HasConstLen {
fn const_name_for(&self, index: usize) -> Option<&'static str>;
fn name_id_for(&self, index: usize) -> Option<u64>;
}
impl HasNameIdTuple for () {
fn const_name_for(&self, _index: usize) -> Option<&'static str> {
None
}
fn name_id_for(&self, _index: usize) -> Option<u64> {
None
}
}
impl<Head, Tail> HasNameIdTuple for (Head, Tail)
where
Head: HasNameId,
Tail: HasNameIdTuple,
{
fn const_name_for(&self, index: usize) -> Option<&'static str> {
if index == 0 {
Some(self.0.const_name())
} else {
self.1.const_name_for(index - 1)
}
}
fn name_id_for(&self, index: usize) -> Option<u64> {
if index == 0 {
Some(self.0.name_id())
} else {
self.1.name_id_for(index - 1)
}
}
}
pub trait MatchFirstType {
fn match_first_type<T: 'static>(&self) -> Option<&T>;
fn match_first_type_mut<T: 'static>(&mut self) -> Option<&mut T>;
}
impl MatchFirstType for () {
fn match_first_type<T: 'static>(&self) -> Option<&T> {
None
}
fn match_first_type_mut<T: 'static>(&mut self) -> Option<&mut T> {
None
}
}
impl<Head, Tail> MatchFirstType for (Head, Tail)
where
Head: 'static,
Tail: MatchFirstType,
{
fn match_first_type<T: 'static>(&self) -> Option<&T> {
if TypeId::of::<T>() == TypeId::of::<Head>() {
unsafe { (addr_of!(self.0) as *const T).as_ref() }
} else {
self.1.match_first_type::<T>()
}
}
fn match_first_type_mut<T: 'static>(&mut self) -> Option<&mut T> {
if TypeId::of::<T>() == TypeId::of::<Head>() {
unsafe { (addr_of_mut!(self.0) as *mut T).as_mut() }
} else {
self.1.match_first_type_mut::<T>()
}
}
}
pub trait MatchType {
fn match_type<T: 'static, FN: FnMut(&T)>(&self, f: &mut FN);
fn match_type_mut<T: 'static, FN: FnMut(&mut T)>(&mut self, f: &mut FN);
}
impl MatchType for () {
fn match_type<T: 'static, FN: FnMut(&T)>(&self, _: &mut FN) {}
fn match_type_mut<T: 'static, FN: FnMut(&mut T)>(&mut self, _: &mut FN) {}
}
impl<Head, Tail> MatchType for (Head, Tail)
where
Head: 'static,
Tail: MatchType,
{
fn match_type<T: 'static, FN: FnMut(&T)>(&self, f: &mut FN) {
if TypeId::of::<T>() == TypeId::of::<Head>() {
f(unsafe { (addr_of!(self.0) as *const T).as_ref() }.unwrap());
}
self.1.match_type::<T, FN>(f);
}
fn match_type_mut<T: 'static, FN: FnMut(&mut T)>(&mut self, f: &mut FN) {
if TypeId::of::<T>() == TypeId::of::<Head>() {
f(unsafe { (addr_of_mut!(self.0) as *mut T).as_mut() }.unwrap());
}
self.1.match_type_mut::<T, FN>(f);
}
}
pub trait Named {
fn name(&self) -> &str;
}
pub trait NamedTuple: HasConstLen {
fn name(&self, index: usize) -> Option<&str>;
}
impl NamedTuple for () {
fn name(&self, _index: usize) -> Option<&str> {
None
}
}
impl<Head, Tail> NamedTuple for (Head, Tail)
where
Head: Named,
Tail: NamedTuple,
{
fn name(&self, index: usize) -> Option<&str> {
if index == 0 {
Some(self.0.name())
} else {
self.1.name(index - 1)
}
}
}
pub trait MatchName {
fn match_name<T>(&self, name: &str) -> Option<&T>;
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T>;
}
impl MatchName for () {
fn match_name<T>(&self, _name: &str) -> Option<&T> {
None
}
fn match_name_mut<T>(&mut self, _name: &str) -> Option<&mut T> {
None
}
}
impl<Head, Tail> MatchName for (Head, Tail)
where
Head: Named,
Tail: MatchName,
{
fn match_name<T>(&self, name: &str) -> Option<&T> {
if type_eq::<Head, T>() && name == self.0.name() {
unsafe { (addr_of!(self.0) as *const T).as_ref() }
} else {
self.1.match_name::<T>(name)
}
}
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
if type_eq::<Head, T>() && name == self.0.name() {
unsafe { (addr_of_mut!(self.0) as *mut T).as_mut() }
} else {
self.1.match_name_mut::<T>(name)
}
}
}
pub trait MatchNameAndType {
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T>;
fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T>;
}
impl MatchNameAndType for () {
fn match_name_type<T: 'static>(&self, _name: &str) -> Option<&T> {
None
}
fn match_name_type_mut<T: 'static>(&mut self, _name: &str) -> Option<&mut T> {
None
}
}
impl<Head, Tail> MatchNameAndType for (Head, Tail)
where
Head: 'static + Named,
Tail: MatchNameAndType,
{
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T> {
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
unsafe { (addr_of!(self.0) as *const T).as_ref() }
} else {
self.1.match_name_type::<T>(name)
}
}
fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T> {
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
unsafe { (addr_of_mut!(self.0) as *mut T).as_mut() }
} else {
self.1.match_name_type_mut::<T>(name)
}
}
}
pub trait Prepend<T> {
type PreprendResult;
#[must_use]
fn prepend(self, value: T) -> (T, Self::PreprendResult);
}
impl<Tail, T> Prepend<T> for Tail {
type PreprendResult = Self;
fn prepend(self, value: T) -> (T, Self::PreprendResult) {
(value, self)
}
}
pub trait Append<T> {
type AppendResult;
#[must_use]
fn append(self, value: T) -> Self::AppendResult;
}
impl<T> Append<T> for () {
type AppendResult = (T, ());
fn append(self, value: T) -> Self::AppendResult {
(value, ())
}
}
impl<Head, Tail, T> Append<T> for (Head, Tail)
where
Tail: Append<T>,
{
type AppendResult = (Head, Tail::AppendResult);
fn append(self, value: T) -> Self::AppendResult {
let (head, tail) = self;
(head, tail.append(value))
}
}
pub trait Merge<T> {
type MergeResult;
#[must_use]
fn merge(self, value: T) -> Self::MergeResult;
}
impl<T> Merge<T> for () {
type MergeResult = T;
fn merge(self, value: T) -> Self::MergeResult {
value
}
}
impl<Head, Tail, T> Merge<T> for (Head, Tail)
where
Tail: Merge<T>,
{
type MergeResult = (Head, Tail::MergeResult);
fn merge(self, value: T) -> Self::MergeResult {
let (head, tail) = self;
(head, tail.merge(value))
}
}
#[macro_export]
#[allow(clippy::items_after_statements)]
macro_rules! tuple_for_each {
($fn_name:ident, $trait_name:path, $tuple_name:ident, $body:expr) => {
#[allow(clippy::items_after_statements)]
mod $fn_name {
pub trait ForEach {
fn for_each(&self);
}
impl ForEach for () {
fn for_each(&self) {}
}
impl<Head, Tail> ForEach for (Head, Tail)
where
Head: $trait_name,
Tail: tuple_list::TupleList + ForEach,
{
#[allow(clippy::redundant_closure_call)]
fn for_each(&self) {
($body)(&self.0);
self.1.for_each();
}
}
}
{
use $fn_name::*;
$tuple_name.for_each();
};
};
}
#[macro_export]
macro_rules! tuple_for_each_mut {
($fn_name:ident, $trait_name:path, $tuple_name:ident, $body:expr) => {
#[allow(clippy::items_after_statements)]
mod $fn_name {
pub trait ForEachMut {
fn for_each_mut(&mut self);
}
impl ForEachMut for () {
fn for_each_mut(&mut self) {}
}
impl<Head, Tail> ForEachMut for (Head, Tail)
where
Head: $trait_name,
Tail: tuple_list::TupleList + ForEachMut,
{
#[allow(clippy::redundant_closure_call)]
fn for_each_mut(&mut self) {
($body)(&mut self.0);
self.1.for_each_mut();
}
}
}
{
use $fn_name::*;
$tuple_name.for_each_mut();
};
};
}
#[cfg(test)]
#[cfg(feature = "std")]
#[test]
#[allow(clippy::items_after_statements)]
pub fn test_macros() {
let mut t = tuple_list!(1, "a");
tuple_for_each!(f1, std::fmt::Display, t, |x| {
println!("{x}");
});
tuple_for_each_mut!(f2, std::fmt::Display, t, |x| {
println!("{x}");
});
}