whatsapp_rust/store/
persistence_manager.rs

1use super::error::StoreError;
2use crate::store::Device;
3use crate::store::traits::Backend;
4use log::{debug, error};
5use std::sync::Arc;
6use tokio::sync::{Mutex, Notify, RwLock};
7use tokio::time::{Duration, sleep};
8
9pub struct PersistenceManager {
10    device: Arc<RwLock<Device>>,
11    backend: Arc<dyn Backend>,
12    dirty: Arc<Mutex<bool>>,
13    save_notify: Arc<Notify>,
14    device_id: Option<i32>, // None for single device mode, Some(id) for multi-account
15}
16
17impl PersistenceManager {
18    /// Create a PersistenceManager with a backend implementation (single device mode)
19    pub async fn new(backend: Arc<dyn Backend>) -> Result<Self, StoreError> {
20        debug!("PersistenceManager: Ensuring device row exists (single-device mode).");
21        // Ensure a device row with id=1 exists; create it if not.
22        let exists = backend
23            .device_exists(1)
24            .await
25            .map_err(|e| StoreError::Database(e.to_string()))?;
26        if !exists {
27            debug!("PersistenceManager: No device row found. Creating new device row.");
28            let id = backend
29                .create_new_device()
30                .await
31                .map_err(|e| StoreError::Database(e.to_string()))?;
32            debug!("PersistenceManager: Created device row with id={id}.");
33        }
34
35        debug!("PersistenceManager: Attempting to load device data via Backend.");
36        let device_data_opt = backend
37            .load_device_data()
38            .await
39            .map_err(|e| StoreError::Database(e.to_string()))?;
40
41        let device = if let Some(serializable_device) = device_data_opt {
42            debug!(
43                "PersistenceManager: Loaded existing device data (PushName: '{}'). Initializing Device.",
44                serializable_device.push_name
45            );
46            let mut dev = Device::new(backend.clone());
47            dev.load_from_serializable(serializable_device);
48            dev
49        } else {
50            debug!("PersistenceManager: No data yet; initializing default Device in memory.");
51            Device::new(backend.clone())
52        };
53
54        Ok(Self {
55            device: Arc::new(RwLock::new(device)),
56            backend,
57            dirty: Arc::new(Mutex::new(false)),
58            save_notify: Arc::new(Notify::new()),
59            device_id: None, // Single device mode
60        })
61    }
62
63    /// Create a PersistenceManager for a specific device ID (multi-account mode)
64    pub async fn new_for_device(
65        device_id: i32,
66        backend: Arc<dyn Backend>,
67    ) -> Result<Self, StoreError> {
68        debug!(
69            "PersistenceManager: Loading device data for device ID {}",
70            device_id
71        );
72
73        // Load device data for this specific device
74        let device_data_opt = backend
75            .load_device_data_for_device(device_id)
76            .await
77            .map_err(|e| StoreError::Database(e.to_string()))?;
78
79        let device = if let Some(serializable_device) = device_data_opt {
80            debug!(
81                "PersistenceManager: Loaded existing device data for device {} (PushName: '{}'). Initializing Device.",
82                device_id, serializable_device.push_name
83            );
84            let mut dev = Device::new(backend.clone());
85            dev.load_from_serializable(serializable_device);
86            dev
87        } else {
88            // This shouldn't happen if the device was just created by StoreManager
89            return Err(StoreError::DeviceNotFound(device_id));
90        };
91
92        Ok(Self {
93            device: Arc::new(RwLock::new(device)),
94            backend,
95            dirty: Arc::new(Mutex::new(false)),
96            save_notify: Arc::new(Notify::new()),
97            device_id: Some(device_id),
98        })
99    }
100
101    /// Get the device ID for this manager (if in multi-account mode)
102    pub fn device_id(&self) -> i32 {
103        self.device_id.unwrap_or(1) // Default to 1 for single device mode
104    }
105
106    /// Check if this manager is in multi-account mode
107    pub fn is_multi_account(&self) -> bool {
108        self.device_id.is_some()
109    }
110
111    pub async fn get_device_arc(&self) -> Arc<RwLock<Device>> {
112        self.device.clone()
113    }
114
115    pub async fn get_device_snapshot(&self) -> Device {
116        self.device.read().await.clone()
117    }
118
119    pub fn backend(&self) -> Arc<dyn Backend> {
120        self.backend.clone()
121    }
122
123    pub async fn modify_device<F, R>(&self, modifier: F) -> R
124    where
125        F: FnOnce(&mut Device) -> R,
126    {
127        let mut device_guard = self.device.write().await;
128        let result = modifier(&mut device_guard);
129
130        let mut dirty_guard = self.dirty.lock().await;
131        *dirty_guard = true;
132        self.save_notify.notify_one();
133
134        result
135    }
136
137    async fn save_to_disk(&self) -> Result<(), StoreError> {
138        let mut dirty_guard = self.dirty.lock().await;
139        if *dirty_guard {
140            debug!("Device state is dirty, saving to disk.");
141            let device_guard = self.device.read().await;
142            let serializable_device = device_guard.to_serializable();
143            drop(device_guard);
144
145            // If this PersistenceManager is associated with a specific device_id,
146            // use the device-aware save path to ensure we update the correct row.
147            if let Some(device_id) = self.device_id {
148                self.backend
149                    .save_device_data_for_device(device_id, &serializable_device)
150                    .await
151                    .map_err(|e| StoreError::Database(e.to_string()))?;
152            } else {
153                self.backend
154                    .save_device_data(&serializable_device)
155                    .await
156                    .map_err(|e| StoreError::Database(e.to_string()))?;
157            }
158            *dirty_guard = false;
159            debug!("Device state saved successfully.");
160        }
161        Ok(())
162    }
163
164    pub fn run_background_saver(self: Arc<Self>, interval: Duration) {
165        tokio::spawn(async move {
166            loop {
167                tokio::select! {
168                    _ = self.save_notify.notified() => {
169                        debug!("Save notification received.");
170                    }
171                    _ = sleep(interval) => {}
172                }
173
174                if let Err(e) = self.save_to_disk().await {
175                    error!("Error saving device state in background: {e}");
176                }
177            }
178        });
179        debug!("Background saver task started with interval {interval:?}");
180    }
181}
182
183use super::commands::{DeviceCommand, apply_command_to_device};
184
185impl PersistenceManager {
186    pub async fn process_command(&self, command: DeviceCommand) {
187        self.modify_device(|device| {
188            apply_command_to_device(device, command);
189        })
190        .await;
191    }
192}