1use core::ops::ControlFlow;
4
5use super::Behavior;
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 ActionReducer<A: Address, Sends, New> {
26 effects: Effects<A, Sends, New>,
27 transitions: usize,
28}
29
30impl<A: Address, Sends: SendAlgebra, New> Default for ActionReducer<A, Sends, New> {
31 fn default() -> Self {
32 Self {
33 effects: Effects {
34 sends: Sends::empty(),
35 creates: Vec::new(),
36 },
37 transitions: 0,
38 }
39 }
40}
41
42impl<A: Address, Sends: SendAlgebra, New> ActionReducer<A, Sends, New> {
43 #[must_use]
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn push<Birth: BirthMode<Child = New>>(
51 &mut self,
52 actions: Actions<A, Never, Sends, Birth>,
53 ) -> ControlFlow<Exit<A>> {
54 self.transitions += 1;
55 self.effects.sends.append(actions.sends);
56 self.effects.creates.extend(actions.creates);
57 match actions.become_ {
58 Step::Continue => ControlFlow::Continue(()),
59 Step::Goto(never) => match never {},
60 Step::Stop(exit) => ControlFlow::Break(exit),
61 }
62 }
63
64 #[must_use]
65 pub fn finish(self, exit: Option<Exit<A>>) -> Folded<A, Sends, New> {
66 Folded {
67 effects: self.effects,
68 exit,
69 transitions: self.transitions,
70 }
71 }
72}
73
74#[allow(
80 clippy::type_complexity,
81 reason = "the result exposes every behavior-owned effect and child seat"
82)]
83pub fn fold_events<B>(
84 behavior: &mut B,
85 events: impl IntoIterator<Item = B::Event>,
86) -> Result<Folded<B::Addr, B::Sends, <B::Birth as BirthMode>::Child>, B::Error>
87where
88 B: Behavior<Ph = Never>,
89{
90 let mut reducer = ActionReducer::new();
91 if let ControlFlow::Break(exit) = reducer.push(behavior.init()?) {
92 return Ok(reducer.finish(Some(exit)));
93 }
94
95 let result = events.into_iter().try_fold((), |(), event| {
96 let actions = match behavior.transition(event) {
97 Ok(actions) => actions,
98 Err(error) => return ControlFlow::Break(Err(error)),
99 };
100 match reducer.push(actions) {
101 ControlFlow::Continue(()) => ControlFlow::Continue(()),
102 ControlFlow::Break(exit) => ControlFlow::Break(Ok(exit)),
103 }
104 });
105
106 match result {
107 ControlFlow::Continue(()) => Ok(reducer.finish(None)),
108 ControlFlow::Break(Ok(exit)) => Ok(reducer.finish(Some(exit))),
109 ControlFlow::Break(Err(error)) => Err(error),
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::{Acted, Actions, Delivery, MailAddr, NoBirths, Pure, Recipient, User};
117
118 #[test]
119 fn event_fold_short_circuits_and_accepts_capturing_transitions() {
120 let stop_at = 3;
121 let mut behavior = Pure::from_fn(
122 0_u8,
123 move |sum: &mut u8,
124 _from: MailAddr,
125 message: u8|
126 -> Acted<
127 MailAddr,
128 Never,
129 Vec<Delivery<MailAddr, u8>>,
130 NoBirths,
131 Never,
132 > {
133 *sum += message;
134 let sends = vec![Delivery::new(Recipient::global(MailAddr(9)), *sum)];
135 Ok(Actions::new(
136 sends,
137 Vec::new(),
138 if *sum >= stop_at {
139 Step::Stop(Exit::Normal)
140 } else {
141 Step::Continue
142 },
143 ))
144 },
145 );
146
147 let folded = fold_events(
148 &mut behavior,
149 [
150 User::new(MailAddr(1), 1),
151 User::new(MailAddr(1), 2),
152 User::new(MailAddr(1), 100),
153 ],
154 )
155 .unwrap();
156
157 assert_eq!(folded.transitions, 3); assert_eq!(folded.effects.sends.len(), 2);
159 assert!(matches!(folded.exit, Some(Exit::Normal)));
160 assert_eq!(behavior.state().state, 3);
161 }
162}