multiversx_sc/tuple_util/
nested_tuples.rs

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/// A tuple of the form (A, (B, (... (N, ())))).
///
/// It is always terminated with a unit.
pub trait NestedTuple {}

impl NestedTuple for () {}

impl<Head, Tail> NestedTuple for (Head, Tail) where Tail: NestedTuple {}

/// Allows to append at the end of a nested tuple list.
pub trait NestedTupleAppend<T> {
    type Output;

    fn append(self, t: T) -> Self::Output;
}

impl<T> NestedTupleAppend<T> for () {
    type Output = (T, ());

    fn append(self, t: T) -> Self::Output {
        (t, ())
    }
}

impl<Head, Tail, T> NestedTupleAppend<T> for (Head, Tail)
where
    Tail: NestedTupleAppend<T>,
{
    type Output = (Head, Tail::Output);

    fn append(self, t: T) -> Self::Output {
        (self.0, self.1.append(t))
    }
}

/// Defines conversion of a nested tuple list to a regular tuple.
pub trait NestedTupleFlatten: NestedTuple {
    type Flattened;
    type Unpacked;

    /// Converts a nested tuple list to a regular tuple.
    fn flatten(self) -> Self::Flattened;

    /// Same as `flatten`, converts a nested tuple list to a regular tuple,
    /// but additionally, it unpacks singleton tuples into their content (`(item,)` -> `item`).
    fn flatten_unpack(self) -> Self::Unpacked;
}

impl NestedTupleFlatten for () {
    type Flattened = ();
    type Unpacked = ();

    fn flatten(self) -> Self::Flattened {}
    fn flatten_unpack(self) -> Self::Unpacked {}
}

impl<T> NestedTupleFlatten for (T, ()) {
    type Flattened = (T,);
    type Unpacked = T;

    fn flatten(self) -> Self::Flattened {
        (self.0,)
    }

    fn flatten_unpack(self) -> Self::Unpacked {
        self.0
    }
}

macro_rules! tuple_list_type {
    () => ( () );
    ($i:ty)  => ( ($i, ()) );
    ($i:ty, $($e:ty),*)  => ( ($i, tuple_list_type!($($e),*)) );
}

macro_rules! unnest {
    (($layer:expr); ($($v:expr),*); ($u:ident, $($us:ident,)*)) => {
        unnest!(($layer . 1); ($($v,)* $layer . 0); ($($us,)*))
    };
    (($layer:expr); ($($v:expr),*); ()) => { ($($v,)*) };
}

macro_rules! flatten_impl {
    ($(($t:ident $($ts:ident)+))+) => {
        $(
            impl<$t,$($ts),+> NestedTupleFlatten for tuple_list_type!($t,$($ts),+) {
                type Flattened = ($t,$($ts),+);
                type Unpacked = ($t,$($ts),+);

                fn flatten(self) -> Self::Flattened {
                    unnest!((self); (); ($t, $($ts,)*))
                }

                fn flatten_unpack(self) -> Self::Unpacked {
                    self.flatten()
                }
            }
        )+
    }
}

flatten_impl! {
    (T1 T2)
    (T1 T2 T3)
    (T1 T2 T3 T4)
    (T1 T2 T3 T4 T5)
    (T1 T2 T3 T4 T5 T6)
    (T1 T2 T3 T4 T5 T6 T7)
    (T1 T2 T3 T4 T5 T6 T7 T8)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15)
    (T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_flatten() {
        let flat2 = (1, (2, ())).flatten();
        assert_eq!(flat2, (1, 2));

        let n3 = (1u8, (2u16, (3u32, ())));
        let flat3 = n3.flatten();
        assert_eq!(flat3, (1u8, 2u16, 3u32));

        let n4 = n3.append(4u64);
        let flat4 = n4.flatten();
        assert_eq!(flat4, (1u8, 2u16, 3u32, 4u64));
    }
}