diff options
Diffstat (limited to 'src-tauri/src/storage.rs')
| -rw-r--r-- | src-tauri/src/storage.rs | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs index 8e2f148..2e726a3 100644 --- a/src-tauri/src/storage.rs +++ b/src-tauri/src/storage.rs @@ -2,6 +2,8 @@ use std::fs; use std::path::PathBuf; use serde::{Deserialize, Serialize}; +use crate::state::TimerPhase; + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Settings { pub work_duration_secs: u64, @@ -32,10 +34,22 @@ pub struct Task { } #[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TimerSnapshot { + pub phase: TimerPhase, + pub remaining_secs: u64, + pub total_secs: u64, + pub running: bool, + pub session_count: u32, + pub current_task_id: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppData { pub settings: Settings, pub tasks: Vec<Task>, pub current_task_id: Option<String>, + #[serde(default)] + pub timer_snapshot: Option<TimerSnapshot>, } impl Default for AppData { @@ -44,6 +58,7 @@ impl Default for AppData { settings: Settings::default(), tasks: Vec::new(), current_task_id: None, + timer_snapshot: None, } } } @@ -77,7 +92,100 @@ pub fn save(app_data_dir: &PathBuf, data: &AppData) -> Result<(), String> { let tmp_path = app_data_dir.join("data.json.tmp"); fs::write(&tmp_path, contents) .map_err(|e| format!("Failed to write temp data file: {}", e))?; + + if cfg!(windows) { + if path.exists() { + fs::remove_file(&path) + .map_err(|e| format!("Failed to remove existing data file: {}", e))?; + } + } + fs::rename(&tmp_path, &path) .map_err(|e| format!("Failed to finalize data file: {}", e))?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::{load, save, AppData, Settings, Task, TimerSnapshot}; + use crate::state::TimerPhase; + use std::fs; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn temp_dir(label: &str) -> std::path::PathBuf { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("solstice-storage-{label}-{unique}")) + } + + #[test] + fn load_preserves_persisted_timer_snapshot() { + let dir = temp_dir("load-timer-snapshot"); + fs::create_dir_all(&dir).unwrap(); + + let json = serde_json::json!({ + "settings": { + "work_duration_secs": 1500, + "short_break_secs": 300, + "long_break_secs": 900, + "sessions_before_long_break": 4 + }, + "tasks": [], + "current_task_id": "task-1", + "timer_snapshot": { + "phase": "short_break", + "remaining_secs": 120, + "total_secs": 300, + "running": false, + "session_count": 2, + "current_task_id": "task-1" + } + }) + .to_string(); + fs::write(dir.join("data.json"), json).unwrap(); + + let data = load(&dir); + + let snapshot = data.timer_snapshot.expect("expected timer snapshot"); + assert_eq!(snapshot.phase, TimerPhase::ShortBreak); + assert_eq!(snapshot.remaining_secs, 120); + assert_eq!(snapshot.total_secs, 300); + assert_eq!(snapshot.session_count, 2); + assert_eq!(snapshot.current_task_id.as_deref(), Some("task-1")); + } + + #[test] + fn save_persists_timer_snapshot() { + let dir = temp_dir("save-timer-snapshot"); + let data = AppData { + settings: Settings::default(), + tasks: vec![Task { + id: "task-1".to_string(), + name: "Deep work".to_string(), + total_sessions: 4, + remaining_sessions: 3, + completed: false, + created_at: "2026-06-09T00:00:00Z".to_string(), + }], + current_task_id: Some("task-1".to_string()), + timer_snapshot: Some(TimerSnapshot { + phase: TimerPhase::Work, + remaining_secs: 600, + total_secs: 1500, + running: true, + session_count: 1, + current_task_id: Some("task-1".to_string()), + }), + }; + + save(&dir, &data).unwrap(); + let reloaded = load(&dir); + + let snapshot = reloaded.timer_snapshot.expect("expected timer snapshot"); + assert!(snapshot.running); + assert_eq!(snapshot.remaining_secs, 600); + assert_eq!(snapshot.total_secs, 1500); + } +} |
