use serde::{Deserialize, Serialize}; use crate::settings::SettingsService; // ── domain types ───────────────────────────────────────────────────────────── /// A single open tab that should be restored on next launch. #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct WorkspaceTab { pub connection_id: i64, pub protocol: String, pub position: i32, } /// The full workspace state persisted between sessions. #[derive(Debug, Serialize, Deserialize, Clone)] pub struct WorkspaceSnapshot { pub tabs: Vec, } // ── service ─────────────────────────────────────────────────────────────────── const SNAPSHOT_KEY: &str = "workspace_snapshot"; const CLEAN_SHUTDOWN_KEY: &str = "clean_shutdown"; pub struct WorkspaceService { settings: SettingsService, } impl WorkspaceService { pub fn new(settings: SettingsService) -> Self { Self { settings } } /// Serialize `snapshot` to JSON and persist it via the settings store. pub fn save(&self, snapshot: &WorkspaceSnapshot) -> Result<(), String> { let json = serde_json::to_string(snapshot) .map_err(|e| format!("workspace::save: failed to serialize snapshot: {e}"))?; self.settings.set(SNAPSHOT_KEY, &json) } /// Load and deserialize the last saved workspace snapshot. /// /// Returns `None` if no snapshot has been saved yet or if deserialization /// fails (treated as a fresh start rather than a hard error). pub fn load(&self) -> Option { let json = self.settings.get(SNAPSHOT_KEY)?; serde_json::from_str(&json) .map_err(|e| eprintln!("workspace::load: failed to deserialize snapshot: {e}")) .ok() } /// Record that the application is shutting down cleanly. /// /// Call this just before the Tauri window closes normally. On the next /// startup, `was_clean_shutdown()` will return `true` and the crash- /// recovery path can be skipped. pub fn mark_clean_shutdown(&self) -> Result<(), String> { self.settings.set(CLEAN_SHUTDOWN_KEY, "true") } /// Returns `true` if the last session ended with a clean shutdown. /// /// Returns `false` (crash / dirty shutdown) when the key is absent, empty, /// or set to any value other than `"true"`. pub fn was_clean_shutdown(&self) -> bool { self.settings .get(CLEAN_SHUTDOWN_KEY) .map(|v| v == "true") .unwrap_or(false) } /// Remove the clean-shutdown flag. /// /// Call this at startup — immediately after reading `was_clean_shutdown()`. /// The flag is only valid for the single launch following the shutdown that /// wrote it; clearing it ensures a subsequent crash is detected correctly. pub fn clear_clean_shutdown(&self) -> Result<(), String> { self.settings.delete(CLEAN_SHUTDOWN_KEY) } }