Skip to main content

chamber_backup/
service.rs

1use crate::manager::{BackupManager, VaultOperations};
2use chamber_vault::BackupConfig;
3use std::sync::{Arc, Mutex};
4use std::thread;
5use std::time::Duration;
6pub struct BackgroundService<V: VaultOperations + Send + 'static> {
7    backup_manager: Arc<Mutex<BackupManager<V>>>,
8    running: Arc<Mutex<bool>>,
9}
10
11impl<V: VaultOperations + Send + 'static> BackgroundService<V> {
12    pub fn new(vault: V, config: BackupConfig) -> Self {
13        let backup_manager = Arc::new(Mutex::new(BackupManager::new(vault, config)));
14        let running = Arc::new(Mutex::new(false));
15
16        Self {
17            backup_manager,
18            running,
19        }
20    }
21
22    /// Starts the backup monitoring thread if it is not already running.
23    ///
24    /// This function initiates a thread that periodically checks whether a backup
25    /// operation is needed. It acquires a lock on `self.running` to ensure that
26    /// only one monitoring thread can run at a time. Once started, the thread will
27    /// perform the following steps in a loop:
28    /// 1. Check the current state of the `running` flag to determine if the thread
29    ///    should continue execution.
30    /// 2. Attempt to acquire a lock on the `backup_manager` to access the backup logic.
31    /// 3. If the backup manager determines a backup is needed, attempt to execute the
32    ///    backup. If the backup operation fails, an error message will be printed to
33    ///    standard error. (Optional: Notification logic can be implemented here.)
34    /// 4. Sleep for one hour before the next backup check.
35    ///
36    /// ## Remarks
37    /// - The function uses `thread::spawn` to create a separate thread for backup
38    ///   checks, enabling it to run independently of the main thread.
39    /// - The sleep interval is hardcoded to 1 hour (`3600` seconds).
40    /// - Errors during locking (`self.running` or `backup_manager`) are handled with
41    ///   `.expect` to provide immediate feedback when locks fail to acquire.
42    /// - Clippy warnings for the use of `.expect` are explicitly disabled, as they
43    ///   are considered acceptable in this context to handle critical locking issues.
44    ///
45    /// ## Panics
46    /// The function may panic in the following cases:
47    /// - If acquiring the lock on `self.running` or `self.backup_manager` fails.
48    /// - If any lock operation encounters a thread-poisoning error.
49    ///
50    /// ## Thread Safety
51    /// - The function ensures proper synchronization by using a `Mutex` for the
52    ///   `running` flag and `backup_manager`.
53    /// - The `running` and `backup_manager` are wrapped in `Arc` (Atomically Reference Counted),
54    ///   allowing shared ownership across threads.
55    ///
56    /// ## Lifetime
57    /// - The thread will run continuously, checking the backup condition every hour, until
58    ///   the `running` flag is set to `false`.
59    ///
60    /// ## Dependencies
61    /// - `std::thread` for spawning the thread and sleeping.
62    /// - `std::sync::{Arc, Mutex}` for synchronization.
63    /// - `std::time::Duration` for specifying the sleep duration.
64    ///
65    /// ## Notes
66    /// - Before calling `start`, ensure that the `BackupService` instance has been correctly
67    ///   initialized with a `backup_manager` capable of handling the `backup_if_needed` logic.
68    /// ```
69    pub fn start(&self) {
70        {
71            #[allow(clippy::expect_used)]
72            let mut running = self.running.lock().expect("Unable to acquire the lock for running");
73            if *running {
74                return; // Already running
75            }
76            *running = true;
77        }
78
79        let backup_manager = Arc::clone(&self.backup_manager);
80        let running = Arc::clone(&self.running);
81
82        #[allow(clippy::expect_used)]
83        thread::spawn(move || {
84            while *running.lock().expect("Unable to acquire the lock for running") {
85                // Check for backup every hour
86                if let Ok(mut manager) = backup_manager.lock() {
87                    if let Err(e) = manager.backup_if_needed() {
88                        eprintln!("Backup failed: {e}");
89                        // Could send notification here
90                    }
91                }
92
93                // Sleep for 1 hour
94                thread::sleep(Duration::from_secs(3600));
95            }
96        });
97    }
98
99    /// Stops the current process or thread associated with the instance by setting the `running` flag
100    /// to `false`. This effectively signals that the process or thread should terminate its execution.
101    ///
102    /// # Behavior
103    /// - Acquires a lock on the `self.running` mutex to safely modify the shared `running` state.
104    /// - If the lock cannot be acquired, it will panic with the message "Unable to get the lock (running)".
105    /// - Updates the `running` boolean to `false` to indicate that the process or thread should stop.
106    ///
107    /// # Panics
108    /// - This method will panic if it fails to acquire the lock for `self.running`. This panic may
109    ///   occur due to a poisoned lock or other concurrency issues.
110    ///
111    /// # Allowances
112    /// - The `clippy::expect_used` lint is explicitly allowed to permit the use of `.expect()` for
113    ///   descriptive error handling in the context of this critical locking operation.
114    #[allow(clippy::expect_used)]
115    pub fn stop(&self) {
116        let mut running = self.running.lock().expect("Unable to acquire the lock for running");
117        *running = false;
118    }
119}