1use crate::config::state_dir;
13use jiff::civil::Date;
14use serde::{Deserialize, Serialize};
15use std::collections::BTreeSet;
16use std::path::{Path, PathBuf};
17
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "lowercase")]
21pub enum Weather {
22 #[default]
23 Clear,
24 Haze,
25 Fog,
26}
27
28impl Weather {
29 pub fn heavy(self) -> u32 {
31 match self {
32 Weather::Clear => 2,
33 Weather::Haze => 1,
34 Weather::Fog => 0,
35 }
36 }
37
38 pub fn room(self) -> u32 {
40 match self {
41 Weather::Clear => 100,
42 Weather::Haze => 60,
43 Weather::Fog => 30,
44 }
45 }
46
47 pub fn parse(text: &str) -> Weather {
48 match text {
49 "haze" => Weather::Haze,
50 "fog" => Weather::Fog,
51 _ => Weather::Clear,
52 }
53 }
54}
55
56pub const FOG_MINUTES: u32 = 15;
58
59#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
60pub struct Today {
61 #[serde(default)]
63 pub date: String,
64 #[serde(default)]
65 pub weather: Weather,
66 #[serde(default)]
68 pub aside: BTreeSet<String>,
69 #[serde(skip)]
71 pub read: crate::filelock::Read,
72}
73
74impl Today {
75 pub fn default_path() -> PathBuf {
76 state_dir().join("today.toml")
77 }
78
79 pub fn load(path: &Path, today: Date) -> Today {
81 let text = std::fs::read_to_string(path).unwrap_or_default();
82 let saved: Today = toml::from_str(&text).unwrap_or_default();
83 let read = crate::filelock::Read::of(&text);
84 if saved.date == today.to_string() { Today { read, ..saved } } else { Today { date: today.to_string(), read, ..Today::default() } }
85 }
86
87 pub fn save(&self, path: &Path) -> Result<(), String> {
90 crate::filelock::save_merged(path, &self.read, self)
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn a_new_day_starts_clean() {
100 let path = std::env::temp_dir().join(format!("sioul-today-{}.toml", std::process::id()));
101 let day: Date = "2026-10-03".parse().unwrap();
102 let mut today = Today::load(&path, day);
103 today.weather = Weather::Fog;
104 today.aside.insert("letter".into());
105 today.save(&path).unwrap();
106 assert_eq!(Today::load(&path, day), today);
107 let tomorrow = Today::load(&path, "2026-10-04".parse().unwrap());
108 assert_eq!((tomorrow.weather, tomorrow.aside.len()), (Weather::Clear, 0));
109 std::fs::remove_file(&path).unwrap();
110 }
111}