1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#![warn(warnings)]
#![no_std]
#![doc = include_str!("../README.md")]

#[macro_export]
macro_rules! tuple_len {
    ( ($($a:expr),+ $(,)?) ) => { $crate::tuple_len!(1usize, $($a,)+) };
    ( $len:expr, $a:expr, $($rest_a:expr,)+ ) => { $crate::tuple_len!($len + 1usize, $($rest_a,)+) };
    ( $len:expr, $a:expr, ) => { $len };
    ( () ) => { 0usize };
    ( $tuple:ident ) => { $crate::len($tuple) };
    ( &$tuple:ident ) => { $crate::len(&$tuple) };
}

#[allow(clippy::len_without_is_empty)]
pub trait TupleLen {
    fn len(&self) -> usize;
}

macro_rules! count {
    () => (0usize);
    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}

macro_rules! tuple_impl {
    ($( $T:ident ),*) => {
        impl<$( $T, )*> TupleLen for ($( $T, )*) {
            #[inline]
            fn len(&self) -> usize {
                count!($( $T )*)
            }
        }

        impl<$( $T, )*> TupleLen for &($( $T, )*) {
            #[inline]
            fn len(&self) -> usize {
                count!($( $T )*)
            }
        }
    }
}

tuple_impl!();
tuple_impl!(A);
tuple_impl!(A, B);
tuple_impl!(A, B, C);
tuple_impl!(A, B, C, D);
tuple_impl!(A, B, C, D, E);
tuple_impl!(A, B, C, D, E, F);
tuple_impl!(A, B, C, D, E, F, G);
tuple_impl!(A, B, C, D, E, F, G, H);
tuple_impl!(A, B, C, D, E, F, G, H, I);
tuple_impl!(A, B, C, D, E, F, G, H, I, J);
tuple_impl!(A, B, C, D, E, F, G, H, I, J, K);
tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

#[inline]
pub fn len(tuple: impl TupleLen) -> usize {
    tuple.len()
}

#[cfg(test)]
mod tests {
    #[test]
    fn r#macro() {
        let _x: u8;

        assert_eq!(tuple_len!(()), 0);
        assert_eq!(tuple_len!((1)), 1);
        assert_eq!(tuple_len!((1,)), 1);
        assert_eq!(tuple_len!(((1, 1))), 1);
        assert_eq!(tuple_len!((_x, _x)), 2);
        assert_eq!(tuple_len!((_x, 1, _x)), 3);
        assert_eq!(tuple_len!((_x, _x, Some("foo"), || {})), 4);
    }

    #[test]
    fn macro_ident() {
        let tuple = ();

        assert_eq!(crate::tuple_len!(tuple), 0);
        assert_eq!(crate::tuple_len!(&tuple), 0);
    }

    #[test]
    fn r#trait() {
        use crate::TupleLen;

        let _x: u8 = 0;

        assert_eq!(().len(), 0);
        assert_eq!((1,).len(), 1);
        assert_eq!((_x, _x).len(), 2);
        assert_eq!((_x, 1, _x).len(), 3);
        assert_eq!((_x, _x, Some("foo"), || {}).len(), 4);
    }

    #[test]
    fn function() {
        let _x: u8 = 0;

        assert_eq!(crate::len(()), 0);
        assert_eq!(crate::len(&()), 0);
        assert_eq!(crate::len((1,)), 1);
        assert_eq!(crate::len((_x, _x)), 2);
        assert_eq!(crate::len((_x, 1, _x)), 3);
        assert_eq!(crate::len((_x, _x, Some("foo"), || {})), 4);
    }
}