sim_lib_control/coroutine.rs
1use sim_kernel::Ref;
2
3/// Identifies which of a coroutine's two cooperating lanes yielded a value.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum CoroutineLane {
6 /// The first lane.
7 First,
8 /// The second lane.
9 Second,
10}
11
12/// Outcome of resuming a [`Coroutine`]: a yielded value, or exhaustion.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub enum CoroutineStep {
15 /// A lane yielded a value and control returned to the driver.
16 Yielded {
17 /// The lane that produced this value.
18 lane: CoroutineLane,
19 /// The yielded value.
20 value: Ref,
21 },
22 /// Both lanes are drained; the coroutine has nothing left to yield.
23 Exhausted,
24}
25
26/// Two cooperating value streams that yield by alternating between lanes.
27///
28/// Models symmetric coroutine control: each [`Coroutine::resume`] hands control
29/// to the next lane, falling through to the other when one is drained.
30///
31/// # Examples
32///
33/// ```
34/// use sim_kernel::{Ref, Symbol};
35/// use sim_lib_control::{Coroutine, CoroutineLane, CoroutineStep};
36///
37/// let a = Ref::Symbol(Symbol::new("a"));
38/// let b = Ref::Symbol(Symbol::new("b"));
39/// let mut co = Coroutine::alternating(vec![a.clone()], vec![b.clone()]);
40/// assert_eq!(
41/// co.resume(),
42/// CoroutineStep::Yielded { lane: CoroutineLane::First, value: a }
43/// );
44/// assert_eq!(
45/// co.resume(),
46/// CoroutineStep::Yielded { lane: CoroutineLane::Second, value: b }
47/// );
48/// assert_eq!(co.resume(), CoroutineStep::Exhausted);
49/// ```
50#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct Coroutine {
52 first: Vec<Ref>,
53 second: Vec<Ref>,
54 first_index: usize,
55 second_index: usize,
56 next_lane: CoroutineLane,
57}
58
59impl Coroutine {
60 /// Builds a coroutine that alternates yields between the `first` and
61 /// `second` lanes, starting with the first.
62 pub fn alternating(first: Vec<Ref>, second: Vec<Ref>) -> Self {
63 Self {
64 first,
65 second,
66 first_index: 0,
67 second_index: 0,
68 next_lane: CoroutineLane::First,
69 }
70 }
71
72 /// Resumes the coroutine, yielding the next value from the active lane (or
73 /// the other lane if the active one is drained), or
74 /// [`CoroutineStep::Exhausted`] when both are empty.
75 pub fn resume(&mut self) -> CoroutineStep {
76 let step = match self.next_lane {
77 CoroutineLane::First => self.resume_first().or_else(|| self.resume_second()),
78 CoroutineLane::Second => self.resume_second().or_else(|| self.resume_first()),
79 };
80 step.unwrap_or(CoroutineStep::Exhausted)
81 }
82
83 fn resume_first(&mut self) -> Option<CoroutineStep> {
84 let value = self.first.get(self.first_index).cloned()?;
85 self.first_index += 1;
86 self.next_lane = CoroutineLane::Second;
87 Some(CoroutineStep::Yielded {
88 lane: CoroutineLane::First,
89 value,
90 })
91 }
92
93 fn resume_second(&mut self) -> Option<CoroutineStep> {
94 let value = self.second.get(self.second_index).cloned()?;
95 self.second_index += 1;
96 self.next_lane = CoroutineLane::First;
97 Some(CoroutineStep::Yielded {
98 lane: CoroutineLane::Second,
99 value,
100 })
101 }
102}