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
//! FSM ingress.

use super::*;

impl<P: Copy, R: Copy, const D: Dep> I<VrH<P, R>, D> {
    /// Runs a finite state machine described by `f`, accepting successive ingress payloads until `f` returns true for
    /// `done_next`. Then, outputs the resulting FSM state and reset.
    ///
    /// This allows you to accumulate successive ingress payloads into the internal FSM state until it is ready to be
    /// transmitted.
    ///
    /// - Payload: Controlled by the combinator's behavior.
    /// - Resolver: The ingress ready signal is controlled by the combinator's behavior. The inner value `R` of the
    ///     resolver is preserved.
    ///
    /// | Interface | Ingress      | Egress       |
    /// | :-------: | ------------ | ------------ |
    /// |  **Fwd**  | `HOption<P>` | `HOption<S>` |
    /// |  **Bwd**  | `Ready<R>`   | `Ready<R>`   |
    ///
    /// # Detailed explanation
    ///
    /// ## Parameters
    ///
    /// - `init`: The initial state for the FSM.
    /// - `f`: The function that describes the FSM. If `let (s_next, done_next) = f(p, r, s)`,
    ///     - `p`: The ingress payload.
    ///     - `r`: The inner value of the egress resolver.
    ///     - `s`: The current FSM state.
    ///     - `s_next`: The next FSM state.
    ///     - `done_next`: Whether the FSM is done accumulating.
    ///
    /// ## High-level overview of the behavior
    ///
    /// The combinator can be in one of the two cases: **Accumulating** and **Outputting**.
    ///
    /// The combinator is initially in the **Acumulating** case. In this case, it keeps accepting ingress payloads and
    /// runs the FSM, allowing it to accumulate the payloads. When `f` returns true for `done_next`, the combinator will
    /// transition to the **Outputting** case next cycle.
    ///
    /// In the **Outputting** case, the combinator outputs the resulting FSM state and blocks the ingress. When the
    /// egress transfer of the state happens, it will transition back to the **Accumulating** case next cycle.
    ///
    /// ## Detailed behavior
    ///
    /// > NOTE: The description below assumes the following naming convention. Here, `s` is the FSM state, and `done`
    /// > represents the current case the combinator is in. The description is organized sligtly differently from the
    /// > actual implementation for better clarity.
    /// >
    /// > ```ignore
    /// > // an implementation of `fsm_ingress`
    /// > self.fsm((init, false), |ip, er, (s, done)| {
    /// >     // ... (the description below would fit here)
    /// >     (ep, ir, (s_next, done_next))
    /// > })
    /// > ```
    ///
    /// - **Accumulating** (`done == false`)
    ///     - Do not produce an egress payload: `ep = None`
    ///     - Accept ingress payloads: `ir = (true, er.inner)`
    ///     - If an ingress transfer happens (`if it`),
    ///         - Run `f`: `let (s_next_f, done_next_f) = f(ip.unwrap(), er.inner, s)`
    ///         - Update the FSM state next cycle: `s_next = s_next_f`
    ///         - Remain in the current case or transition to the **Outputting** case next cycle, depending on the
    ///             returned value: `done_next = done_next_f`
    ///     - If no ingress transfer happens (`else`),
    ///         - Do not update the FSM state: `s_next = s`
    ///         - Remain in the current case: `done_next = done`
    /// - **Outputting** (`done == true`)
    ///     - Output the FSM state: `ep = Some(s)`
    ///     - Block ingress payloads: `ir = (false, er.inner)`
    ///     - If an egress transfer happens (`if et`)
    ///         - Reset the FSM state next cycle: `s_next = init`
    ///         - Transition to the **Accumulating** case next cycle: `done_next = false`
    ///     - If no egress transfer happens (`else`)
    ///         - Do not change the FSM state: `s_next = s`
    ///         - Remain in the current case: `done_next = done`
    pub fn fsm_ingress<S: Copy>(self, init: S, f: impl Fn(P, R, S) -> (S, bool)) -> I<VrH<S, R>, { Dep::Helpful }> {
        unsafe {
            self.fsm::<(S, bool), { Dep::Helpful }, VrH<S, R>>((init, false), |ip, er, (s, done)| {
                let ir = Ready::new(!done, er.inner);

                let it = ip.is_some() && !done;
                let et = er.ready && done;

                let ep = if done { Some(s) } else { None };

                let s_next = if it {
                    f(ip.unwrap(), er.inner, s)
                } else if et {
                    (init, false)
                } else {
                    (s, done)
                };

                (ep, ir, s_next)
            })
        }
    }
}