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
use typle::typle;
trait Process {
type State;
type Output;
fn process(state: Self::State) -> Result<Self::Output, Error>;
}
#[typle(Tuple for 0..=2)]
pub enum ProcessState<T>
where
T: Tuple,
T<_>: Process<Output = u64>,
{
// `typle_variant!` creates a variant for each component. The variant will have a number
// added to the variant name here. `S2(Option<T2::State>, [u64; 2])`
S = typle_variant!(i in ..T::MAX => Option<T<{i}>::State>, [u64; i]),
// U2 {u: [u32; 2]}
U = typle_variant! {i in ..Tuple::MAX => u: [u32; i]},
// V2
V = typle_variant![..Tuple::MAX],
Done([u64; Tuple::MAX]),
}
#[typle(Tuple for 0..=3)]
impl<T> Default for ProcessState<T<{ ..T::MAX }>>
where
T: Tuple,
T<_>: Process<Output = u64>,
{
fn default() -> Self {
// Const-if allows false branches to contain invalid code. In this case state S0 does not
// exist for the empty tuple implementation. An alternative to using the `typle_const!` here
// is to set the typle! macro range to 1..=3 and implement `Default` separately for `()`.
if typle_const!(T::LEN == 0) {
Self::Done([0; 3])
} else {
Self::S0(None, [])
}
}
}
#[typle(Tuple for 0..=3)]
impl Process for T
where
T: Tuple,
T<_>: Process<Output = u64>,
{
type State = ProcessState<T<{ ..T::MAX }>>;
type Output = [u64; T::LEN];
fn process(state: Self::State) -> Result<Self::Output, Error> {
for typle_index!(i) in 0..T::LEN {
if let Self::State::S::<typle_ident!(i)>(inner_state, output) = state {
match self.tuple[[i]].process(inner_state) {
Err(e) => {
return Err(e);
}
Ok(value) => {
// The iteration variable `i` can be used on other const contexts:
let mut new_output = <[u64; i + 1]>::default();
output
.into_iter()
.chain(std::iter::once(value))
.enumerate()
// shadowing of `i` doesn't work correctly so use a different name:
.for_each(|(j, bs)| new_output[j] = bs);
// Often a standard `if` can be used, but we need a const-if here because
// the state S::<typle_ident!(i + 1)> does not exist on the last iteration.
// In that case, the second branch is never taken, and will likely get
// optimized out, but it still needs to compile.
if typle_const!(i + 1 == T::LEN) {
state = Self::State::Done(new_output);
} else {
state = Self::State::S::<typle_ident!(i + 1)>(None, new_output);
}
}
}
}
}
if let Self::State::Done(output) = state {
return output;
}
unreachable!();
}
}
#[typle(Tuple for 3..=3)]
impl Process for T
where
T: Tuple,
T<_>: Process<Output = u64>,
{
// Test that the number of components can be a different range
type State = ProcessState<T<{ 3.. }>>;
type Output = ProcessState<T<{ 1.. }>>;
fn process(state: Self::State) -> Result<Self::Output, Error> {
let x = Self::State::S::<typle_ident!(0)>((), None);
unreachable!();
}
}