pgdo/coordinate/
guard.rs

1use super::{lock, shutdown, startup, CoordinateError, Subject};
2
3enum GuardDropMode {
4    Stop,
5    Destroy,
6}
7
8/// Smart pointer around a [`Subject`] that ensures the subject is stopped or
9/// destroyed when it goes out of scope.
10///
11/// Errors when stopping or destroying the subject are logged but otherwise
12/// ignored.
13pub struct Guard<SUBJECT>
14where
15    SUBJECT: Subject,
16{
17    mode: GuardDropMode,
18    lock: Option<lock::LockedFileShared>,
19    subject: SUBJECT,
20}
21
22impl<T> Guard<T>
23where
24    T: Subject,
25{
26    /// Starts the given subject and returns the guard.
27    pub fn startup<L: Into<lock::UnlockedFile>>(
28        lock: L,
29        subject: T,
30        options: T::Options<'_>,
31    ) -> Result<Self, CoordinateError<T::Error>> {
32        let lock = startup(lock.into(), &subject, options)?;
33        Ok(Self { mode: GuardDropMode::Stop, lock: lock.into(), subject })
34    }
35}
36
37impl<T> Guard<T>
38where
39    T: Subject,
40{
41    /// Configures the guard to *stop* the subject when it goes out of scope.
42    #[must_use]
43    pub fn and_stop(mut self) -> Self {
44        self.mode = GuardDropMode::Stop;
45        self
46    }
47
48    /// Configures the guard to *destroy* the subject when it goes out of scope.
49    #[must_use]
50    pub fn and_destroy(mut self) -> Self {
51        self.mode = GuardDropMode::Destroy;
52        self
53    }
54}
55
56impl<T> std::ops::Deref for Guard<T>
57where
58    T: Subject,
59{
60    type Target = T;
61
62    fn deref(&self) -> &Self::Target {
63        &self.subject
64    }
65}
66
67impl<T> Drop for Guard<T>
68where
69    T: Subject,
70{
71    fn drop(&mut self) {
72        if let Some(lock) = self.lock.take() {
73            let result = match &self.mode {
74                GuardDropMode::Stop => shutdown::<T, _, _>(lock, || self.subject.stop()),
75                GuardDropMode::Destroy => shutdown::<T, _, _>(lock, || self.subject.destroy()),
76            };
77            match (&self.mode, result) {
78                (GuardDropMode::Stop, Ok(_)) => (),
79                (GuardDropMode::Stop, Err(err)) => {
80                    log::error!("Error stopping subject: {err}");
81                }
82                (GuardDropMode::Destroy, Ok(_)) => (),
83                (GuardDropMode::Destroy, Err(err)) => {
84                    log::error!("Error destroying subject: {err}");
85                }
86            }
87        }
88    }
89}