Skip to main content

entracte_lib/
screen_time_store.rs

1use 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, PartialEq, Eq)]
11#[serde(default)]
12pub struct ScreenTimeSnapshot {
13    pub date: String,
14    pub seconds: u64,
15    pub last_reminder_epoch_secs: Option<u64>,
16}
17
18pub fn load(path: &Path) -> ScreenTimeSnapshot {
19    match fs::read_to_string(path) {
20        Ok(text) => serde_json::from_str(&text).unwrap_or_else(|e| {
21            error!(
22                "screen_time_store: failed to parse {}: {e} — using defaults",
23                path.display()
24            );
25            ScreenTimeSnapshot::default()
26        }),
27        Err(e) if e.kind() == io::ErrorKind::NotFound => ScreenTimeSnapshot::default(),
28        Err(e) => {
29            error!(
30                "screen_time_store: failed to read {}: {e} — using defaults",
31                path.display()
32            );
33            ScreenTimeSnapshot::default()
34        }
35    }
36}
37
38pub fn save(path: &Path, snapshot: &ScreenTimeSnapshot) -> io::Result<()> {
39    let body = serde_json::to_string_pretty(snapshot).map_err(io::Error::other)?;
40    write_user_only(path, body.as_bytes())
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use crate::test_support::{temp_dir, TempDir};
47
48    fn temp_screen_time_file() -> (TempDir, std::path::PathBuf) {
49        let dir = temp_dir();
50        let path = dir.path().join("screen_time.json");
51        (dir, path)
52    }
53
54    #[test]
55    fn load_missing_returns_default() {
56        let (_dir, path) = temp_screen_time_file();
57        let s = load(&path);
58        assert!(s.date.is_empty());
59        assert_eq!(s.seconds, 0);
60        assert!(s.last_reminder_epoch_secs.is_none());
61    }
62
63    #[test]
64    fn save_and_load_round_trip() {
65        let (_dir, path) = temp_screen_time_file();
66        let snap = ScreenTimeSnapshot {
67            date: "2026-05-15".to_string(),
68            seconds: 1234,
69            last_reminder_epoch_secs: Some(1_700_000_000),
70        };
71        save(&path, &snap).unwrap();
72        let loaded = load(&path);
73        assert_eq!(loaded, snap);
74    }
75
76    #[test]
77    fn load_corrupt_returns_default() {
78        let (_dir, path) = temp_screen_time_file();
79        fs::write(&path, "{not valid json").unwrap();
80        let loaded = load(&path);
81        assert!(loaded.date.is_empty());
82        assert_eq!(loaded.seconds, 0);
83    }
84
85    #[test]
86    fn save_creates_parent_dirs() {
87        let dir = temp_dir();
88        let path = dir.path().join("a").join("b").join("screen_time.json");
89        save(&path, &ScreenTimeSnapshot::default()).unwrap();
90        assert!(path.exists());
91    }
92}