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