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 impl Behavior for Accumulator {
165 type Addr = MailAddr;
166 type Msg = u8;
167 type Event = User<MailAddr, u8>;
168 type Sends = Vec<Delivery<Sink>>;
169 type Ph = Never;
170 type Error = Never;
171 type Birth = NoBirths;
172
173 fn transition(
174 &mut self,
175 _: crate::ActiveTurn,
176 event: Self::Event,
177 ) -> crate::BehaviorActed<Self> {
178 self.sum += event.message;
179 let sends = vec![Delivery::new(Recipient::global(MailAddr(9)), self.sum)];
180 Ok(Actions::new(
181 sends,
182 Vec::new(),
183 if self.sum >= self.stop_at {
184 Step::Stop(Stopped)
185 } else {
186 Step::Continue
187 },
188 ))
189 }
190 }
191
192 impl Behavior for Sink {
193 type Addr = MailAddr;
194 type Msg = u8;
195 type Event = User<MailAddr, u8>;
196 type Sends = Vec<Never>;
197 type Ph = Never;
198 type Error = Never;
199 type Birth = NoBirths;
200
201 fn init(&mut self, _: crate::InitializationTurn) -> crate::BehaviorActed<Self> {
202 Ok(Actions::cont())
203 }
204
205 fn transition(
206 &mut self,
207 _: crate::ActiveTurn,
208 _: Self::Event,
209 ) -> crate::BehaviorActed<Self> {
210 Ok(Actions::cont())
211 }
212 }
213
214 #[test]
215 fn event_fold_short_circuits_and_accepts_capturing_transitions() {
216 let behavior = Accumulator { sum: 0, stop_at: 3 };
217
218 let folded = fold_events(
219 behavior,
220 [
221 User::new(MailAddr(1), 1),
222 User::new(MailAddr(1), 2),
223 User::new(MailAddr(1), 100),
224 ],
225 )
226 .unwrap();
227
228 assert_eq!(folded.transitions, 3); assert_eq!(folded.effects.sends.len(), 2);
230 assert!(folded.stopped);
231 }
232}