1use core::ops::ControlFlow;
4
5use super::{Behavior, delegate_transition, initialize};
6use crate::Exit;
7use crate::actor::{Address, BirthMode, Create};
8use crate::effects::{Actions, SendAlgebra};
9use crate::next::{Never, Step};
10
11pub struct Effects<A: Address, Sends, New> {
13 pub sends: Sends,
14 pub creates: Vec<Create<A, New>>,
15}
16
17pub struct Folded<A: Address, Sends, New> {
19 pub effects: Effects<A, Sends, New>,
20 pub exit: Option<Exit<A>>,
21 pub transitions: usize,
22}
23
24pub struct FoldFailure<A: Address, Sends, New, E> {
26 pub effects: Effects<A, Sends, New>,
27 pub error: E,
28 pub transitions: usize,
29}
30
31impl<A: Address, Sends, New, E: core::fmt::Debug> core::fmt::Debug
32 for FoldFailure<A, Sends, New, E>
33{
34 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35 formatter
36 .debug_struct("FoldFailure")
37 .field("error", &self.error)
38 .field("transitions", &self.transitions)
39 .finish_non_exhaustive()
40 }
41}
42
43pub struct ActionReducer<A: Address, Sends, New> {
45 effects: Effects<A, Sends, New>,
46 transitions: usize,
47}
48
49impl<A: Address, Sends: SendAlgebra, New> Default for ActionReducer<A, Sends, New> {
50 fn default() -> Self {
51 Self {
52 effects: Effects {
53 sends: Sends::empty(),
54 creates: Vec::new(),
55 },
56 transitions: 0,
57 }
58 }
59}
60
61impl<A: Address, Sends: SendAlgebra, New> ActionReducer<A, Sends, New> {
62 #[must_use]
63 pub fn new() -> Self {
64 Self::default()
65 }
66
67 pub fn push<Birth: BirthMode<Child = New>>(
70 &mut self,
71 actions: Actions<A, Never, Sends, Birth>,
72 ) -> ControlFlow<Exit<A>> {
73 self.transitions += 1;
74 self.effects.sends.append(actions.sends);
75 self.effects.creates.extend(actions.creates);
76 match actions.become_ {
77 Step::Continue => ControlFlow::Continue(()),
78 Step::Goto(never) => match never {},
79 Step::Stop(exit) => ControlFlow::Break(exit),
80 }
81 }
82
83 #[must_use]
84 pub fn finish(self, exit: Option<Exit<A>>) -> Folded<A, Sends, New> {
85 Folded {
86 effects: self.effects,
87 exit,
88 transitions: self.transitions,
89 }
90 }
91}
92
93#[allow(
99 clippy::type_complexity,
100 reason = "the result exposes every behavior-owned effect and child seat"
101)]
102pub fn fold_events<B>(
103 mut behavior: B,
104 events: impl IntoIterator<Item = B::Event>,
105) -> Result<
106 Folded<B::Addr, B::Sends, <B::Birth as BirthMode>::Child>,
107 FoldFailure<B::Addr, B::Sends, <B::Birth as BirthMode>::Child, B::Error>,
108>
109where
110 B: Behavior<Ph = Never>,
111{
112 let mut reducer = ActionReducer::new();
113 let initialization = match initialize(&mut behavior) {
114 Ok(actions) => actions,
115 Err(error) => {
116 let folded = reducer.finish(None);
117 return Err(FoldFailure {
118 effects: folded.effects,
119 error,
120 transitions: folded.transitions,
121 });
122 }
123 };
124 if let ControlFlow::Break(exit) = reducer.push(initialization) {
125 return Ok(reducer.finish(Some(exit)));
126 }
127
128 let result = events.into_iter().try_fold((), |(), event| {
129 let actions = match delegate_transition(&mut behavior, event) {
130 Ok(actions) => actions,
131 Err(error) => return ControlFlow::Break(Err(error)),
132 };
133 match reducer.push(actions) {
134 ControlFlow::Continue(()) => ControlFlow::Continue(()),
135 ControlFlow::Break(exit) => ControlFlow::Break(Ok(exit)),
136 }
137 });
138
139 match result {
140 ControlFlow::Continue(()) => Ok(reducer.finish(None)),
141 ControlFlow::Break(Ok(exit)) => Ok(reducer.finish(Some(exit))),
142 ControlFlow::Break(Err(error)) => {
143 let folded = reducer.finish(None);
144 Err(FoldFailure {
145 effects: folded.effects,
146 error,
147 transitions: folded.transitions,
148 })
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use crate::{Actions, Behavior, Delivery, MailAddr, NoBirths, Recipient, User};
157
158 struct Sink;
159
160 struct Accumulator {
161 sum: u8,
162 stop_at: u8,
163 }
164
165 impl Behavior for Accumulator {
166 type Addr = MailAddr;
167 type Msg = u8;
168 type Event = User<MailAddr, u8>;
169 type Sends = Vec<Delivery<Sink>>;
170 type Ph = Never;
171 type Error = Never;
172 type Birth = NoBirths;
173
174 fn transition(
175 &mut self,
176 _: crate::ActiveTurn,
177 event: Self::Event,
178 ) -> crate::BehaviorActed<Self> {
179 self.sum += event.message;
180 let sends = vec![Delivery::new(Recipient::global(MailAddr(9)), self.sum)];
181 Ok(Actions::new(
182 sends,
183 Vec::new(),
184 if self.sum >= self.stop_at {
185 Step::Stop(Exit::Normal)
186 } else {
187 Step::Continue
188 },
189 ))
190 }
191 }
192
193 impl Behavior for Sink {
194 type Addr = MailAddr;
195 type Msg = u8;
196 type Event = User<MailAddr, u8>;
197 type Sends = Vec<Never>;
198 type Ph = Never;
199 type Error = Never;
200 type Birth = NoBirths;
201
202 fn init(&mut self, _: crate::InitializationTurn) -> crate::BehaviorActed<Self> {
203 Ok(Actions::cont())
204 }
205
206 fn transition(
207 &mut self,
208 _: crate::ActiveTurn,
209 _: Self::Event,
210 ) -> crate::BehaviorActed<Self> {
211 Ok(Actions::cont())
212 }
213 }
214
215 #[test]
216 fn event_fold_short_circuits_and_accepts_capturing_transitions() {
217 let behavior = Accumulator { sum: 0, stop_at: 3 };
218
219 let folded = fold_events(
220 behavior,
221 [
222 User::new(MailAddr(1), 1),
223 User::new(MailAddr(1), 2),
224 User::new(MailAddr(1), 100),
225 ],
226 )
227 .unwrap();
228
229 assert_eq!(folded.transitions, 3); assert_eq!(folded.effects.sends.len(), 2);
231 assert!(matches!(folded.exit, Some(Exit::Normal)));
232 }
233}