1use std::time::Duration;
5
6use tokio::time::Instant;
7
8use crate::behavior::{Address, Behavior, BirthMode, Births};
9use crate::deadlined::{At, AtReaction};
10use crate::protocol::AtId;
11use crate::shutdown::{FinalizeOnShutdown, ShutdownReaction, StopOnShutdown};
12use crate::stashing::{StashRoute, Stashing};
13use crate::supervising::{RestartPolicy, Strategy, Supervising, SupervisionFailureReaction};
14use crate::verdict::Never;
15use crate::watching::{LinkReaction, Watching};
16use crate::{Actions, Base, Fsm, Move, SendAlgebra, State};
17
18const DEFAULT_STRATEGY: Strategy = Strategy::OneForOne;
19const DEFAULT_POLICY: RestartPolicy = RestartPolicy::Transient;
20const DEFAULT_BUDGET: (u32, Duration) = (1, Duration::from_secs(5));
21
22fn identity_nonce<N: From<u64>>(index: usize) -> N {
23 N::from(u64::try_from(index).expect("fleet index fits u64"))
24}
25
26pub struct Spec<B> {
27 behavior: B,
28 next_timer: u64,
29}
30
31impl<S: State<O, Br, E>, O, Br: BirthMode, E> Spec<Base<S, O, Br, E>> {
32 #[must_use]
33 pub fn new(state: S) -> Self {
34 Self {
35 behavior: Base::new(state),
36 next_timer: 0,
37 }
38 }
39}
40
41impl<A, S, M, P, E> Spec<Fsm<A, S, M, P, E>>
42where
43 A: Address,
44 P: Copy + PartialEq,
45{
46 #[must_use]
47 pub fn machine(state: S, phase: P, on: fn(P, &mut S, &M) -> Result<Move<P>, E>) -> Self {
48 Self {
49 behavior: Fsm::new(state, phase, on),
50 next_timer: 0,
51 }
52 }
53}
54
55impl<B: Behavior> Spec<B> {
56 #[must_use]
57 pub fn from_behavior(behavior: B) -> Self {
58 Self {
59 behavior,
60 next_timer: 0,
61 }
62 }
63
64 #[must_use]
65 pub fn build(self) -> B {
66 self.behavior
67 }
68
69 #[must_use]
70 pub fn behavior(&self) -> &B {
71 &self.behavior
72 }
73
74 #[must_use]
76 pub fn stop_on_shutdown(self) -> Spec<StopOnShutdown<B>> {
77 Spec {
78 behavior: StopOnShutdown::new(self.behavior),
79 next_timer: self.next_timer,
80 }
81 }
82
83 #[must_use]
86 pub fn finalize_on_shutdown(
87 self,
88 finalize: ShutdownReaction<B>,
89 ) -> Spec<FinalizeOnShutdown<B>> {
90 Spec {
91 behavior: FinalizeOnShutdown::new(self.behavior, finalize),
92 next_timer: self.next_timer,
93 }
94 }
95
96 #[must_use]
98 pub fn watch(self, peer: B::Addr, on_stopped: LinkReaction<B>) -> Spec<Watching<B>> {
99 Spec {
100 behavior: Watching::new(self.behavior, peer, on_stopped),
101 next_timer: self.next_timer,
102 }
103 }
104
105 #[must_use]
112 pub fn at(self, when: Option<Instant>, on_reached: AtReaction<B>) -> Spec<At<B>> {
113 Spec {
114 behavior: At::new(self.behavior, AtId(self.next_timer), when, on_reached),
115 next_timer: self
116 .next_timer
117 .checked_add(1)
118 .expect("timer identity exhausted"),
119 }
120 }
121
122 #[must_use]
124 pub fn stash(self, route: fn(&B::Msg) -> StashRoute) -> Spec<Stashing<B>>
125 where
126 B: Behavior<Ph = Never>,
127 {
128 Spec {
129 behavior: Stashing::new(self.behavior, route),
130 next_timer: self.next_timer,
131 }
132 }
133
134 #[must_use]
137 pub fn children<C>(self, fleet: (usize, fn(usize) -> C)) -> Spec<Supervising<B, C>>
138 where
139 B: Behavior<Birth = Births<C>>,
140 C: Behavior<Ph = Never, Addr = B::Addr>,
141 <B::Addr as Address>::Nonce: From<u64>,
142 {
143 self.children_with_nonces(identity_nonce, fleet.0, fleet.1)
144 }
145
146 #[must_use]
147 pub fn children_with_nonces<C>(
148 self,
149 nonces: fn(usize) -> <B::Addr as Address>::Nonce,
150 count: usize,
151 build: fn(usize) -> C,
152 ) -> Spec<Supervising<B, C>>
153 where
154 B: Behavior<Birth = Births<C>>,
155 C: Behavior<Ph = Never, Addr = B::Addr>,
156 {
157 Spec {
158 behavior: Supervising::new(
159 self.behavior,
160 nonces,
161 count,
162 build,
163 DEFAULT_STRATEGY,
164 DEFAULT_POLICY,
165 DEFAULT_BUDGET.0,
166 DEFAULT_BUDGET.1,
167 ),
168 next_timer: self.next_timer,
169 }
170 }
171}
172
173impl<B, C> Spec<Supervising<B, C>>
174where
175 B: Behavior<Birth = Births<C>>,
176 C: Behavior<Ph = Never, Addr = B::Addr>,
177{
178 #[must_use]
179 pub fn restart(self, strategy: Strategy) -> Self {
180 Self {
181 behavior: self.behavior.with_strategy(strategy),
182 next_timer: self.next_timer,
183 }
184 }
185
186 #[must_use]
187 pub fn when(self, policy: RestartPolicy) -> Self {
188 Self {
189 behavior: self.behavior.with_policy(policy),
190 next_timer: self.next_timer,
191 }
192 }
193
194 #[must_use]
195 pub fn within(self, maximum: u32, window: Duration) -> Self {
196 Self {
197 behavior: self.behavior.with_budget(maximum, window),
198 next_timer: self.next_timer,
199 }
200 }
201
202 #[must_use]
205 pub fn on_supervision_failure(self, reaction: SupervisionFailureReaction<B>) -> Self {
206 Self {
207 behavior: self.behavior.with_failure_reaction(reaction),
208 next_timer: self.next_timer,
209 }
210 }
211}
212
213impl<B, A, Ph, Sends, Br> Behavior for Spec<B>
214where
215 A: Address + Send,
216 Sends: SendAlgebra,
217 Br: BirthMode,
218 B: Behavior<Addr = A, Ph = Ph, Sends = Sends, Birth = Br> + Send,
219 A::Nonce: Send,
220 B::Msg: Send,
221 B::Event: Send,
222{
223 type Addr = A;
224 type Msg = B::Msg;
225 type Event = B::Event;
226 type Sends = Sends;
227 type Ph = Ph;
228 type Error = B::Error;
229 type Birth = Br;
230
231 async fn init(&mut self) -> Result<Actions<A, Ph, Sends, Br>, B::Error> {
232 self.behavior.init().await
233 }
234
235 async fn step(&mut self, event: B::Event) -> Result<Actions<A, Ph, Sends, Br>, B::Error> {
236 self.behavior.step(event).await
237 }
238}