1use std::time::Duration;
5
6use super::domain::TimerLease;
7use super::event::TimedEvent;
8use crate::Step;
9use crate::behavior::{
10 Actions, Address, Behavior, BirthMode, SendAlgebra, ServiceSends, UserEvent,
11};
12use crate::protocol::{ScheduleAfter, TimeEvent, TimerId};
13use crate::{Inner, Own, SendInput};
14
15pub type ReceiveTimeoutEvent<E> = TimedEvent<E>;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum ReceiveTimeoutError<E> {
20 Inner(E),
22 GenerationExhausted,
29}
30
31pub type ReceiveTimeoutReaction<B> = fn(
32 &mut B,
33) -> Result<
34 Actions<
35 <B as Behavior>::Addr,
36 <B as Behavior>::Ph,
37 <B as Behavior>::Sends,
38 <B as Behavior>::Birth,
39 >,
40 <B as Behavior>::Error,
41>;
42
43pub struct ReceiveTimeoutSends<Sends> {
45 pub behavior: Sends,
46 pub schedules: ServiceSends<ScheduleAfter>,
47}
48
49impl<Sends: SendAlgebra> SendAlgebra for ReceiveTimeoutSends<Sends> {
50 fn empty() -> Self {
51 Self {
52 behavior: Sends::empty(),
53 schedules: ServiceSends::empty(),
54 }
55 }
56
57 fn append(&mut self, other: Self) {
58 self.behavior.append(other.behavior);
59 self.schedules.append(other.schedules);
60 }
61}
62
63impl<Sends> SendInput<ScheduleAfter, Own> for ReceiveTimeoutSends<Sends> {
64 fn emit(&mut self, input: ScheduleAfter) {
65 self.schedules.send(input);
66 }
67}
68
69impl<Sends, Input, Path> SendInput<Input, Inner<Path>> for ReceiveTimeoutSends<Sends>
70where
71 Sends: SendInput<Input, Path>,
72{
73 fn emit(&mut self, input: Input) {
74 <Sends as SendInput<Input, Path>>::emit(&mut self.behavior, input);
75 }
76}
77
78pub type ReceiveTimeoutActions<B> = Actions<
79 <B as Behavior>::Addr,
80 <B as Behavior>::Ph,
81 ReceiveTimeoutSends<<B as Behavior>::Sends>,
82 <B as Behavior>::Birth,
83>;
84
85pub struct ReceiveTimeout<B: Behavior> {
93 inner: B,
94 id: TimerId,
95 after: Duration,
96 timer: TimerLease,
97 on_elapsed: ReceiveTimeoutReaction<B>,
98}
99
100impl<B: Behavior> ReceiveTimeout<B> {
101 #[must_use]
102 pub fn new(
103 inner: B,
104 id: TimerId,
105 after: Duration,
106 on_elapsed: ReceiveTimeoutReaction<B>,
107 ) -> Self {
108 Self {
109 inner,
110 id,
111 after,
112 timer: TimerLease::new(),
113 on_elapsed,
114 }
115 }
116
117 #[must_use]
118 pub fn inner(&self) -> &B {
119 &self.inner
120 }
121
122 fn schedule(&mut self) -> Result<ServiceSends<ScheduleAfter>, ReceiveTimeoutError<B::Error>> {
123 let generation = self
124 .timer
125 .arm()
126 .map_err(|_| ReceiveTimeoutError::GenerationExhausted)?;
127 Ok(ServiceSends::one(ScheduleAfter::new(
128 self.id, generation, self.after,
129 )))
130 }
131
132 fn wrap(
133 actions: Actions<B::Addr, B::Ph, B::Sends, B::Birth>,
134 own: ServiceSends<ScheduleAfter>,
135 ) -> ReceiveTimeoutActions<B> {
136 actions.map_sends(|behavior| ReceiveTimeoutSends {
137 behavior,
138 schedules: own,
139 })
140 }
141
142 fn terminal(actions: &Actions<B::Addr, B::Ph, B::Sends, B::Birth>) -> bool {
143 matches!(actions.become_, Step::Stop(_))
144 }
145}
146
147impl<B, A, Ph, Sends, Br> Behavior for ReceiveTimeout<B>
148where
149 A: Address,
150 Sends: SendAlgebra,
151 Br: BirthMode,
152 B: Behavior<Addr = A, Ph = Ph, Sends = Sends, Birth = Br>,
153 B::Event: TimeEvent,
154{
155 type Addr = A;
156 type Msg = B::Msg;
157 type Event = ReceiveTimeoutEvent<B::Event>;
158 type Sends = ReceiveTimeoutSends<Sends>;
159 type Ph = Ph;
160 type Error = ReceiveTimeoutError<B::Error>;
161 type Birth = Br;
162
163 fn init(&mut self) -> Result<ReceiveTimeoutActions<B>, Self::Error> {
164 let actions = self.inner.init().map_err(ReceiveTimeoutError::Inner)?;
165 let own = if Self::terminal(&actions) {
166 self.timer.disarm();
167 ServiceSends::empty()
168 } else {
169 self.schedule()?
170 };
171 Ok(Self::wrap(actions, own))
172 }
173
174 fn transition(&mut self, event: Self::Event) -> Result<ReceiveTimeoutActions<B>, Self::Error> {
175 match event {
176 ReceiveTimeoutEvent::Elapsed(elapsed)
177 if elapsed.id == self.id && self.timer.accept(elapsed.generation) =>
178 {
179 let actions =
180 (self.on_elapsed)(&mut self.inner).map_err(ReceiveTimeoutError::Inner)?;
181 Ok(Self::wrap(actions, ServiceSends::empty()))
182 }
183 ReceiveTimeoutEvent::Elapsed(elapsed) if elapsed.id == self.id => Ok(Actions::cont()),
184 ReceiveTimeoutEvent::Elapsed(elapsed) => {
185 let Some(inner) = B::Event::time_reached(elapsed) else {
186 return Ok(Actions::cont());
187 };
188 let actions = self
189 .inner
190 .transition(inner)
191 .map_err(ReceiveTimeoutError::Inner)?;
192 if Self::terminal(&actions) {
193 self.timer.disarm();
194 }
195 Ok(Self::wrap(actions, ServiceSends::empty()))
196 }
197 ReceiveTimeoutEvent::Inner(event) => match event.into_user() {
198 Ok(user) => {
199 let event = B::Event::user(user.from, user.message);
200 let actions = self
201 .inner
202 .transition(event)
203 .map_err(ReceiveTimeoutError::Inner)?;
204 let own = if Self::terminal(&actions) {
205 self.timer.disarm();
206 ServiceSends::empty()
207 } else {
208 self.schedule()?
209 };
210 Ok(Self::wrap(actions, own))
211 }
212 Err(service) => {
213 let actions = self
214 .inner
215 .transition(service)
216 .map_err(ReceiveTimeoutError::Inner)?;
217 if Self::terminal(&actions) {
218 self.timer.disarm();
219 }
220 Ok(Self::wrap(actions, ServiceSends::empty()))
221 }
222 },
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::{Acted, Handler, MailAddr, Never, NoBirths, Pure, TimerGeneration, User};
231
232 struct Count(u8);
233
234 impl Handler for Count {
235 type Addr = MailAddr;
236 type Msg = ();
237
238 fn receive(
239 &mut self,
240 _from: MailAddr,
241 (): (),
242 ) -> Acted<MailAddr, Never, Vec<Never>, NoBirths, Never> {
243 self.0 += 1;
244 Ok(Actions::cont())
245 }
246 }
247
248 type Inner = Pure<Count>;
249
250 #[allow(
251 clippy::unnecessary_wraps,
252 reason = "the reaction fixture must implement the fallible reaction signature"
253 )]
254 fn elapsed(_inner: &mut Inner) -> Acted<MailAddr, Never, Vec<Never>, NoBirths, Never> {
255 Ok(Actions::cont())
256 }
257
258 #[tokio::test]
259 async fn exhaustion_follows_the_successful_inner_fold_without_emitting_its_actions() {
260 let mut timeout = ReceiveTimeout::new(
261 Pure::new(Count(0)),
262 TimerId(0),
263 Duration::from_secs(1),
264 elapsed,
265 );
266 timeout.init().unwrap();
267 timeout.timer = TimerLease::idle(TimerGeneration(u64::MAX));
268
269 let result = timeout.transition(ReceiveTimeoutEvent::Inner(User::user(MailAddr(1), ())));
270
271 assert!(matches!(
272 result,
273 Err(ReceiveTimeoutError::GenerationExhausted)
274 ));
275 assert_eq!(timeout.inner().state().0, 1);
276 assert_eq!(timeout.timer.live(), None);
277 }
278}