1use std::time::Duration;
4
5use super::domain::{TimerAdmission, TimerLease};
6use super::event::{TimedEvent, TimedReaction};
7use crate::protocol::{ScheduleAfter, TimerId};
8use behavior::Step;
9use behavior::{
10 Actions, Address, Behavior, BehaviorActed, BirthMode, EventLayer, InterpreterRequests,
11 SendEffects, SendLayer,
12};
13
14pub struct OneShot<B: Behavior> {
40 inner: B,
41 id: TimerId,
42 after: Duration,
43 lease: TimerLease,
44 on_elapsed: TimedReaction<B>,
45}
46
47impl<B: Behavior> OneShot<B> {
48 #[must_use]
50 pub fn new(inner: B, id: TimerId, after: Duration, on_elapsed: TimedReaction<B>) -> Self {
51 Self {
52 inner,
53 id,
54 after,
55 lease: TimerLease::new(),
56 on_elapsed,
57 }
58 }
59
60 fn schedule(&mut self) -> InterpreterRequests<ScheduleAfter> {
61 self.lease
62 .arm()
63 .map_or_else(InterpreterRequests::empty, |generation| {
64 InterpreterRequests::one(ScheduleAfter::new(self.id, generation, self.after))
65 })
66 }
67
68 fn wrap(
69 actions: Actions<behavior::BehaviorAddr<B>, B::Ph, B::Sends, B::Birth>,
70 schedules: InterpreterRequests<ScheduleAfter>,
71 ) -> Actions<
72 behavior::BehaviorAddr<B>,
73 B::Ph,
74 SendLayer<InterpreterRequests<ScheduleAfter>, B::Sends>,
75 B::Birth,
76 > {
77 actions.map_sends(|inner| SendLayer::new(schedules, inner))
78 }
79}
80
81impl<B: Behavior + behavior::BehaviorBase> behavior::BehaviorBase for OneShot<B> {
82 type Base = B::Base;
83
84 fn base(&self) -> &Self::Base {
85 self.inner.base()
86 }
87}
88
89impl<B, A, Ph, Sends, Br> Behavior for OneShot<B>
90where
91 A: Address,
92 Sends: SendEffects + behavior::SendsFor<B::Event>,
93 Br: BirthMode,
94 B: Behavior<Ph = Ph, Sends = Sends, Birth = Br>,
95 B::Protocol: behavior::Protocol<Addr = A>,
96{
97 type Protocol = B::Protocol;
98 type Event = TimedEvent<B::Event>;
99 type Sends = SendLayer<InterpreterRequests<ScheduleAfter>, Sends>;
100 type Ph = Ph;
101 type Error = B::Error;
102 type Birth = Br;
103
104 fn init(&mut self, _: behavior::InitializationTurn) -> BehaviorActed<Self> {
105 let actions = behavior::initialize(&mut self.inner)?;
106 let schedules = if matches!(actions.become_, Step::Stop(_)) {
107 self.lease.disarm();
108 InterpreterRequests::empty()
109 } else {
110 self.schedule()
111 };
112 Ok(Self::wrap(actions, schedules))
113 }
114
115 fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> BehaviorActed<Self> {
116 match event {
117 EventLayer::Owned(elapsed) if elapsed.id == self.id => {
118 match self.lease.accept(elapsed.generation) {
119 TimerAdmission::Accepted => {
120 let actions = (self.on_elapsed)(&mut self.inner);
121 Ok(Self::wrap(actions, InterpreterRequests::empty()))
122 }
123 TimerAdmission::Ignored => Ok(Actions::cont()),
124 }
125 }
126 EventLayer::Owned(_) => Ok(Actions::cont()),
127 EventLayer::Inner(event) => behavior::delegate_transition(&mut self.inner, event)
128 .map(|actions| Self::wrap(actions, InterpreterRequests::empty())),
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::{Activate as _, TimerElapsed};
137 use behavior::{MailAddr, Never, NoBirths, Step, User};
138
139 struct Probe {
140 elapsed: usize,
141 }
142
143 impl behavior::BehaviorBase for Probe {
144 type Base = Self;
145
146 fn base(&self) -> &Self {
147 self
148 }
149 }
150
151 impl behavior::Protocol for Probe {
152 type Addr = MailAddr;
153 type Msg = ();
154 }
155
156 impl Behavior for Probe {
157 type Protocol = Self;
158 type Event = User<MailAddr, ()>;
159 type Sends = Vec<Never>;
160 type Ph = Never;
161 type Error = Never;
162 type Birth = NoBirths;
163
164 fn transition(&mut self, _: behavior::ActiveTurn, _: Self::Event) -> BehaviorActed<Self> {
165 Ok(Actions::cont())
166 }
167 }
168
169 fn mark(probe: &mut Probe) -> Actions<MailAddr, Never, Vec<Never>, NoBirths> {
170 probe.elapsed += 1;
171 Actions::cont()
172 }
173
174 #[test]
175 fn initialization_schedules_then_matching_generation_fires_once() {
176 let delay = Duration::from_millis(20);
177 let initialized = crate::OneShot::new(Probe { elapsed: 0 }, TimerId(7), delay, mark)
178 .initialize()
179 .unwrap();
180 assert!(initialized.actions.sends.inner.is_empty());
181 assert_eq!(
182 initialized.actions.sends.owned.as_slice(),
183 [ScheduleAfter::new(
184 TimerId(7),
185 crate::TimerGeneration(0),
186 delay
187 )]
188 );
189 assert!(initialized.actions.creates.is_empty());
190 assert!(matches!(initialized.actions.become_, Step::Continue));
191
192 let mut active = initialized.behavior;
193 let wrong_id = active
194 .on_path(TimerElapsed::new(TimerId(8), crate::TimerGeneration(0)))
195 .unwrap();
196 assert!(wrong_id.sends == SendLayer::empty());
197 assert_eq!(active.base().elapsed, 0);
198
199 let fired = active
200 .on_path(TimerElapsed::new(TimerId(7), crate::TimerGeneration(0)))
201 .unwrap();
202 assert!(fired.sends == SendLayer::empty());
203 assert_eq!(active.base().elapsed, 1);
204
205 let duplicate = active
206 .on_path(TimerElapsed::new(TimerId(7), crate::TimerGeneration(0)))
207 .unwrap();
208 assert_eq!(duplicate.sends, SendLayer::empty());
209 assert!(duplicate.creates.is_empty());
210 assert!(matches!(duplicate.become_, Step::Continue));
211 assert_eq!(active.base().elapsed, 1);
212 }
213}