entracte_lib/
pause_store.rs1use std::fs;
2use std::io;
3use std::path::Path;
4
5use log::error;
6use serde::{Deserialize, Serialize};
7
8use crate::secure_io::write_user_only;
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11#[serde(default)]
12pub struct PauseSnapshot {
13 pub paused: bool,
14 pub until_epoch_secs: Option<u64>,
15}
16
17pub fn load(path: &Path) -> PauseSnapshot {
18 match fs::read_to_string(path) {
19 Ok(text) => serde_json::from_str(&text).unwrap_or_else(|e| {
20 error!(
21 "pause_store: failed to parse {}: {e} — using defaults",
22 path.display()
23 );
24 PauseSnapshot::default()
25 }),
26 Err(e) if e.kind() == io::ErrorKind::NotFound => PauseSnapshot::default(),
27 Err(e) => {
28 error!(
29 "pause_store: failed to read {}: {e} — using defaults",
30 path.display()
31 );
32 PauseSnapshot::default()
33 }
34 }
35}
36
37pub fn save(path: &Path, snapshot: &PauseSnapshot) -> io::Result<()> {
38 let body = serde_json::to_string_pretty(snapshot).map_err(io::Error::other)?;
39 write_user_only(path, body.as_bytes())
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::test_support::{temp_dir, TempDir};
46
47 fn temp_pause_file() -> (TempDir, std::path::PathBuf) {
48 let dir = temp_dir();
49 let path = dir.path().join("pause.json");
50 (dir, path)
51 }
52
53 #[test]
54 fn load_missing_returns_default() {
55 let (_dir, path) = temp_pause_file();
56 let s = load(&path);
57 assert!(!s.paused);
58 assert!(s.until_epoch_secs.is_none());
59 }
60
61 #[test]
62 fn save_and_load_round_trip_indefinite() {
63 let (_dir, path) = temp_pause_file();
64 let snap = PauseSnapshot {
65 paused: true,
66 until_epoch_secs: None,
67 };
68 save(&path, &snap).unwrap();
69 let loaded = load(&path);
70 assert!(loaded.paused);
71 assert!(loaded.until_epoch_secs.is_none());
72 }
73
74 #[test]
75 fn save_and_load_round_trip_until() {
76 let (_dir, path) = temp_pause_file();
77 let snap = PauseSnapshot {
78 paused: true,
79 until_epoch_secs: Some(1_700_000_000),
80 };
81 save(&path, &snap).unwrap();
82 let loaded = load(&path);
83 assert!(loaded.paused);
84 assert_eq!(loaded.until_epoch_secs, Some(1_700_000_000));
85 }
86
87 #[test]
88 fn save_and_load_round_trip_running() {
89 let (_dir, path) = temp_pause_file();
90 let snap = PauseSnapshot {
91 paused: false,
92 until_epoch_secs: None,
93 };
94 save(&path, &snap).unwrap();
95 let loaded = load(&path);
96 assert!(!loaded.paused);
97 assert!(loaded.until_epoch_secs.is_none());
98 }
99
100 #[test]
101 fn load_corrupt_returns_default() {
102 let (_dir, path) = temp_pause_file();
103 fs::write(&path, "{not valid json").unwrap();
104 let loaded = load(&path);
105 assert!(!loaded.paused);
106 assert!(loaded.until_epoch_secs.is_none());
107 }
108
109 #[test]
110 fn save_creates_parent_dirs() {
111 let dir = temp_dir();
112 let path = dir.path().join("a").join("b").join("pause.json");
113 save(&path, &PauseSnapshot::default()).unwrap();
114 assert!(path.exists());
115 }
116}