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