bastion/
callbacks.rs

1use std::fmt::{self, Debug, Formatter};
2use std::sync::Arc;
3
4#[derive(Debug, Clone)]
5#[allow(dead_code)]
6pub(crate) enum CallbackType {
7    AfterRestart,
8    AfterStop,
9    BeforeRestart,
10    BeforeStart,
11    AfterStart,
12}
13
14#[derive(Default, Clone)]
15/// A set of methods that will get called at different states of
16/// a [`Supervisor`] or [`Children`] life.
17///
18/// # Example
19///
20/// ```rust
21/// # use bastion::prelude::*;
22/// #
23/// # #[cfg(feature = "tokio-runtime")]
24/// # #[tokio::main]
25/// # async fn main() {
26/// #    run();
27/// # }
28/// #
29/// # #[cfg(not(feature = "tokio-runtime"))]
30/// # fn main() {
31/// #    run();
32/// # }
33/// #
34/// # fn run() {
35/// # Bastion::init();
36/// #
37/// Bastion::children(|children| {
38///     let callbacks = Callbacks::new()
39///         .with_before_start(|| println!("Children group started."))
40///         .with_after_stop(|| println!("Children group stopped."));
41///
42///     children
43///         .with_callbacks(callbacks)
44///         .with_exec(|ctx| {
45///             // -- Children group started.
46///             async move {
47///                 // ...
48///                 # Ok(())
49///             }
50///             // -- Children group stopped.
51///         })
52/// }).expect("Couldn't create the children group.");
53/// #
54/// # Bastion::start();
55/// # Bastion::stop();
56/// # Bastion::block_until_stopped();
57/// # }
58/// ```
59///
60/// [`Supervisor`]: crate::supervisor::Supervisor
61/// [`Children`]: crate::children::Children
62pub struct Callbacks {
63    before_start: Option<Arc<dyn Fn() + Send + Sync>>,
64    after_start: Option<Arc<dyn Fn() + Send + Sync>>,
65    before_restart: Option<Arc<dyn Fn() + Send + Sync>>,
66    after_restart: Option<Arc<dyn Fn() + Send + Sync>>,
67    after_stop: Option<Arc<dyn Fn() + Send + Sync>>,
68}
69
70impl Callbacks {
71    /// Creates a new instance of `Callbacks` for
72    /// [`Supervisor::with_callbacks`] or [`Children::with_callbacks`].
73    ///
74    /// # Example
75    ///
76    /// ```rust
77    /// # use bastion::prelude::*;
78    /// #
79    /// # #[cfg(feature = "tokio-runtime")]
80    /// # #[tokio::main]
81    /// # async fn main() {
82    /// #    run();
83    /// # }
84    /// #
85    /// # #[cfg(not(feature = "tokio-runtime"))]
86    /// # fn main() {
87    /// #    run();
88    /// # }
89    /// #
90    /// # fn run() {
91    /// # Bastion::init();
92    /// #
93    /// Bastion::children(|children| {
94    ///     let callbacks = Callbacks::new()
95    ///         .with_before_start(|| println!("Children group started."))
96    ///         .with_after_stop(|| println!("Children group stopped."));
97    ///
98    ///     children
99    ///         .with_callbacks(callbacks)
100    ///         .with_exec(|ctx| {
101    ///             // -- Children group started.
102    ///             async move {
103    ///                 // ...
104    ///                 # Ok(())
105    ///             }
106    ///             // -- Children group stopped.
107    ///         })
108    /// }).expect("Couldn't create the children group.");
109    /// #
110    /// # Bastion::start();
111    /// # Bastion::stop();
112    /// # Bastion::block_until_stopped();
113    /// # }
114    /// ```
115    ///
116    /// [`Supervisor::with_callbacks`]: crate::supervisor::Supervisor::with_callbacks
117    /// [`Children::with_callbacks`]: crate::children::Children::with_callbacks
118    pub fn new() -> Self {
119        Callbacks::default()
120    }
121
122    /// Sets the method that will get called before the [`Supervisor`]
123    /// or [`Children`] is launched if:
124    /// - it was never called before
125    /// - or the supervisor of the supervised element using this callback
126    ///     (or the system) decided to restart it and it was already
127    ///     stopped or killed
128    /// - or the supervisor of the supervised element using this callback
129    ///     (or the system) decided to restart it and it wasn't already
130    ///     stopped or killed but did not have a callback defined using
131    ///     [`with_after_restart`]
132    ///
133    /// # Example
134    ///
135    /// ```rust
136    /// # use bastion::prelude::*;
137    /// #
138    /// # #[cfg(feature = "tokio-runtime")]
139    /// # #[tokio::main]
140    /// # async fn main() {
141    /// #    run();
142    /// # }
143    /// #
144    /// # #[cfg(not(feature = "tokio-runtime"))]
145    /// # fn main() {
146    /// #    run();
147    /// # }
148    /// #
149    /// # fn run() {
150    /// # Bastion::init();
151    /// #
152    /// # Bastion::supervisor(|supervisor| {
153    /// supervisor.children(|children| {
154    ///     let callbacks = Callbacks::new()
155    ///         .with_before_start(|| println!("Children group started."))
156    ///         .with_before_restart(|| println!("Children group restarting."))
157    ///         .with_after_restart(|| println!("Children group restarted."))
158    ///         .with_after_stop(|| println!("Children group stopped."));
159    ///
160    ///     children
161    ///         .with_exec(|ctx| {
162    ///             // -- Children group started.
163    ///             async move {
164    ///                 // ...
165    ///
166    ///                 // This will stop the children group...
167    ///                 Ok(())
168    ///                 // Note that because the children group stopped by itself,
169    ///                 // if its supervisor restarts it, its `before_start` callback
170    ///                 // will get called and not `after_restart`.
171    ///             }
172    ///             // -- Children group stopped.
173    ///         })
174    ///         .with_callbacks(callbacks)
175    /// })
176    /// # }).unwrap();
177    /// #
178    /// # Bastion::start();
179    /// # Bastion::stop();
180    /// # Bastion::block_until_stopped();
181    /// # }
182    /// ```
183    ///
184    /// [`Supervisor`]: crate::supervisor::Supervisor
185    /// [`Children`]: crate::children::Children
186    /// [`with_after_restart`]: Self::with_after_restart
187    pub fn with_before_start<C>(mut self, before_start: C) -> Self
188    where
189        C: Fn() + Send + Sync + 'static,
190    {
191        let before_start = Arc::new(before_start);
192        self.before_start = Some(before_start);
193        self
194    }
195
196    /// Sets the method that will get called right after the [`Supervisor`]
197    /// or [`Children`] is launched.
198    /// This method will be called after the child has subscribed to its distributors and dispatchers.
199    ///
200    /// Once the callback has run, the child has caught up it's message backlog,
201    /// and is waiting for new messages to process.
202    ///
203    /// # Example
204    ///
205    /// ```rust
206    /// # use bastion::prelude::*;
207    /// #
208    /// # #[cfg(feature = "tokio-runtime")]
209    /// # #[tokio::main]
210    /// # async fn main() {
211    /// #    run();
212    /// # }
213    /// #
214    /// # #[cfg(not(feature = "tokio-runtime"))]
215    /// # fn main() {
216    /// #    run();
217    /// # }
218    /// #
219    /// # fn run() {
220    /// # Bastion::init();
221    /// #
222    /// # Bastion::supervisor(|supervisor| {
223    /// supervisor.children(|children| {
224    ///     let callbacks = Callbacks::new()
225    ///         .with_after_start(|| println!("Children group ready to process messages."));
226    ///
227    ///     children
228    ///         .with_exec(|ctx| {
229    ///             // -- Children group started.
230    ///             // with_after_start called
231    ///             async move {
232    ///                 // ...
233    ///
234    ///                 // This will stop the children group...
235    ///                 Ok(())
236    ///                 // Note that because the children group stopped by itself,
237    ///                 // if its supervisor restarts it, its `before_start` callback
238    ///                 // will get called and not `after_restart`.
239    ///             }
240    ///             // -- Children group stopped.
241    ///         })
242    ///         .with_callbacks(callbacks)
243    /// })
244    /// # }).unwrap();
245    /// #
246    /// # Bastion::start();
247    /// # Bastion::stop();
248    /// # Bastion::block_until_stopped();
249    /// # }
250    /// ```
251    ///
252    /// [`Supervisor`]: crate::supervisor::Supervisor
253    /// [`Children`]: crate::children::Children
254    /// [`with_after_restart`]: Self::with_after_restart
255    pub fn with_after_start<C>(mut self, after_start: C) -> Self
256    where
257        C: Fn() + Send + Sync + 'static,
258    {
259        let after_start = Arc::new(after_start);
260        self.after_start = Some(after_start);
261        self
262    }
263
264    /// Sets the method that will get called before the [`Supervisor`]
265    /// or [`Children`] is reset if:
266    /// - the supervisor of the supervised element using this callback
267    ///     (or the system) decided to restart it and it wasn't already
268    ///     stopped or killed
269    ///
270    /// Note that if this callback isn't defined but one was defined using
271    /// [`with_after_stop`], it will get called instead.
272    ///
273    /// # Example
274    ///
275    /// ```rust
276    /// # use bastion::prelude::*;
277    /// #
278    /// # #[cfg(feature = "tokio-runtime")]
279    /// # #[tokio::main]
280    /// # async fn main() {
281    /// #    run();
282    /// # }
283    /// #
284    /// # #[cfg(not(feature = "tokio-runtime"))]
285    /// # fn main() {
286    /// #    run();
287    /// # }
288    /// #
289    /// # fn run() {
290    /// # Bastion::init();
291    /// #
292    /// # Bastion::supervisor(|supervisor| {
293    /// supervisor.children(|children| {
294    ///     let callbacks = Callbacks::new()
295    ///         .with_before_start(|| println!("Children group started."))
296    ///         .with_before_restart(|| println!("Children group restarting."))
297    ///         .with_after_restart(|| println!("Children group restarted."))
298    ///         .with_after_stop(|| println!("Children group stopped."));
299    ///
300    ///     children
301    ///         .with_exec(|ctx| {
302    ///             // Once -- Children group started.
303    ///             // and then -- Children group restarted.
304    ///             async move {
305    ///                 // ...
306    ///
307    ///                 // This will make the children group fault and get
308    ///                 // restarted by its supervisor...
309    ///                 Err(())
310    ///             }
311    ///             // -- Children group restarting.
312    ///             // Note that if a `before_restart` wasn't specified for
313    ///             // this children group, `after_stop` would get called
314    ///             // instead.
315    ///         })
316    ///         .with_callbacks(callbacks)
317    /// })
318    /// # }).unwrap();
319    /// #
320    /// # Bastion::start();
321    /// # Bastion::stop();
322    /// # Bastion::block_until_stopped();
323    /// # }
324    /// ```
325    ///
326    /// [`Supervisor`]: crate::supervisor::Supervisor
327    /// [`Children`]: crate::children::Children
328    /// [`with_after_stop`]: Self::with_after_stop
329    pub fn with_before_restart<C>(mut self, before_restart: C) -> Self
330    where
331        C: Fn() + Send + Sync + 'static,
332    {
333        let before_restart = Arc::new(before_restart);
334        self.before_restart = Some(before_restart);
335        self
336    }
337
338    /// Sets the method that will get called before the [`Supervisor`]
339    /// or [`Children`] is launched if:
340    /// - the supervisor of the supervised element using this callback
341    ///     (or the system) decided to restart it and it wasn't already
342    ///     stopped or killed
343    ///
344    /// Note that if this callback isn't defined but one was defined using
345    /// [`with_before_start`], it will get called instead.
346    ///
347    /// # Example
348    ///
349    /// ```rust
350    /// # use bastion::prelude::*;
351    /// #
352    /// # #[cfg(feature = "tokio-runtime")]
353    /// # #[tokio::main]
354    /// # async fn main() {
355    /// #    run();
356    /// # }
357    /// #
358    /// # #[cfg(not(feature = "tokio-runtime"))]
359    /// # fn main() {
360    /// #    run();
361    /// # }
362    /// #
363    /// # fn run() {
364    /// # Bastion::init();
365    /// #
366    /// # Bastion::supervisor(|supervisor| {
367    /// supervisor.children(|children| {
368    ///     let callbacks = Callbacks::new()
369    ///         .with_before_start(|| println!("Children group started."))
370    ///         .with_before_restart(|| println!("Children group restarting."))
371    ///         .with_after_restart(|| println!("Children group restarted."))
372    ///         .with_after_stop(|| println!("Children group stopped."));
373    ///
374    ///     children
375    ///         .with_exec(|ctx| {
376    ///             // Once -- Children group started.
377    ///             // and then -- Children group restarted.
378    ///             // Note that if a `after_restart` callback wasn't specified
379    ///             // for this children group, `before_restart` would get called
380    ///             // instead.
381    ///             async move {
382    ///                 // ...
383    ///
384    ///                 // This will make the children group fault and get
385    ///                 // restarted by its supervisor...
386    ///                 Err(())
387    ///             }
388    ///             // -- Children group restarting.
389    ///         })
390    ///         .with_callbacks(callbacks)
391    /// })
392    /// # }).unwrap();
393    /// #
394    /// # Bastion::start();
395    /// # Bastion::stop();
396    /// # Bastion::block_until_stopped();
397    /// # }
398    /// ```
399    ///
400    /// [`Supervisor`]: crate::supervisor::Supervisor
401    /// [`Children`]: crate::children::Children
402    /// [`with_before_start`]: Self::method.with_before_start
403    pub fn with_after_restart<C>(mut self, after_restart: C) -> Self
404    where
405        C: Fn() + Send + Sync + 'static,
406    {
407        let after_restart = Arc::new(after_restart);
408        self.after_restart = Some(after_restart);
409        self
410    }
411
412    /// Sets the method that will get called after the [`Supervisor`]
413    /// or [`Children`] is stopped or killed if:
414    /// - the supervisor of the supervised element using this callback
415    ///     (or the system) decided to stop (not restart nor kill) it and
416    ///     it wasn't already stopped or killed
417    /// - or the supervisor or children group using this callback
418    ///     stopped or killed itself or was stopped or killed by a
419    ///     reference to it
420    /// - or the supervisor of the supervised element using this callback
421    ///     (or the system) decided to restart it and it wasn't already
422    ///     stopped or killed but did not have a callback defined using
423    ///     [`with_before_restart`]
424    ///
425    /// # Example
426    ///
427    /// ```rust
428    /// # use bastion::prelude::*;
429    /// #
430    /// # #[cfg(feature = "tokio-runtime")]
431    /// # #[tokio::main]
432    /// # async fn main() {
433    /// #    run();
434    /// # }
435    /// #
436    /// # #[cfg(not(feature = "tokio-runtime"))]
437    /// # fn main() {
438    /// #    run();
439    /// # }
440    /// #
441    /// # fn run() {
442    /// # Bastion::init();
443    /// #
444    /// # Bastion::supervisor(|supervisor| {
445    /// supervisor.children(|children| {
446    ///     let callbacks = Callbacks::new()
447    ///         .with_before_start(|| println!("Children group started."))
448    ///         .with_before_restart(|| println!("Children group restarting."))
449    ///         .with_after_restart(|| println!("Children group restarted."))
450    ///         .with_after_stop(|| println!("Children group stopped."));
451    ///
452    ///     children
453    ///         .with_exec(|ctx| {
454    ///             // -- Children group started.
455    ///             async move {
456    ///                 // ...
457    ///
458    ///                 // This will stop the children group...
459    ///                 Ok(())
460    ///             }
461    ///             // -- Children group stopped.
462    ///             // Note that because the children group stopped by itself,
463    ///             // it its supervisor restarts it, its `before_restart` callback
464    ///             // will not get called.
465    ///         })
466    ///         .with_callbacks(callbacks)
467    /// })
468    /// # }).unwrap();
469    /// #
470    /// # Bastion::start();
471    /// # Bastion::stop();
472    /// # Bastion::block_until_stopped();
473    /// # }
474    /// ```
475    ///
476    /// [`Supervisor`]: crate::supervisor::Supervisor
477    /// [`Children`]: crate::children::Children
478    /// [`with_before_restart`]: Self::with_before_restart
479    pub fn with_after_stop<C>(mut self, after_stop: C) -> Self
480    where
481        C: Fn() + Send + Sync + 'static,
482    {
483        let after_stop = Arc::new(after_stop);
484        self.after_stop = Some(after_stop);
485        self
486    }
487
488    /// Returns whether a callback was defined using [`with_before_start`].
489    ///
490    /// # Example
491    ///
492    /// ```rust
493    /// # use bastion::prelude::*;
494    /// #
495    /// let callbacks = Callbacks::new()
496    ///     .with_before_start(|| println!("Children group started."));
497    ///
498    /// assert!(callbacks.has_before_start());
499    /// ```
500    ///
501    /// [`with_before_start`]: Self::with_before_start
502    pub fn has_before_start(&self) -> bool {
503        self.before_start.is_some()
504    }
505
506    /// Returns whether a callback was defined using [`with_before_restart`].
507    ///
508    /// # Example
509    ///
510    /// ```rust
511    /// # use bastion::prelude::*;
512    /// #
513    /// let callbacks = Callbacks::new()
514    ///     .with_before_restart(|| println!("Children group restarting."));
515    ///
516    /// assert!(callbacks.has_before_restart());
517    /// ```
518    ///
519    /// [`with_before_restart`]: Self::with_before_restart
520    pub fn has_before_restart(&self) -> bool {
521        self.before_restart.is_some()
522    }
523
524    /// Returns whether a callback was defined using [`with_after_restart`].
525    ///
526    /// # Example
527    ///
528    /// ```rust
529    /// # use bastion::prelude::*;
530    /// #
531    /// let callbacks = Callbacks::new()
532    ///     .with_after_restart(|| println!("Children group restarted."));
533    ///
534    /// assert!(callbacks.has_after_restart());
535    /// ```
536    ///
537    /// [`with_after_restart`]: Self::with_after_restart
538    pub fn has_after_restart(&self) -> bool {
539        self.after_restart.is_some()
540    }
541
542    /// Returns whether a callback was defined using [`with_after_stop`].
543    ///
544    /// # Example
545    ///
546    /// ```rust
547    /// # use bastion::prelude::*;
548    /// #
549    /// let callbacks = Callbacks::new()
550    ///     .with_after_stop(|| println!("Children group stopped."));
551    ///
552    /// assert!(callbacks.has_after_stop());
553    /// ```
554    ///
555    /// [`with_after_stop`]: Self::with_after_stop
556    pub fn has_after_stop(&self) -> bool {
557        self.after_stop.is_some()
558    }
559
560    pub(crate) fn before_start(&self) {
561        if let Some(before_start) = &self.before_start {
562            before_start()
563        }
564    }
565
566    pub(crate) fn after_start(&self) {
567        if let Some(after_start) = &self.after_start {
568            after_start()
569        }
570    }
571
572    pub(crate) fn before_restart(&self) {
573        if let Some(before_restart) = &self.before_restart {
574            before_restart()
575        } else {
576            self.after_stop()
577        }
578    }
579
580    pub(crate) fn after_restart(&self) {
581        if let Some(after_restart) = &self.after_restart {
582            after_restart()
583        } else {
584            self.before_start()
585        }
586    }
587
588    pub(crate) fn after_stop(&self) {
589        if let Some(after_stop) = &self.after_stop {
590            after_stop()
591        }
592    }
593}
594
595impl Debug for Callbacks {
596    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
597        fmt.debug_struct("Callbacks")
598            .field("before_start", &self.before_start.is_some())
599            .field("before_restart", &self.before_start.is_some())
600            .field("after_restart", &self.before_start.is_some())
601            .field("after_stop", &self.before_start.is_some())
602            .finish()
603    }
604}