1use chrono_tz::Tz;
2use serde::{Deserialize, Deserializer};
3
4fn deserialize_timezone<'de, D>(deserializer: D) -> Result<Option<Tz>, D::Error>
5where
6 D: Deserializer<'de>,
7{
8 let s: Option<String> = Option::deserialize(deserializer)?;
9 match s {
10 Some(s) => s.parse().map(Some).map_err(serde::de::Error::custom),
11 None => Ok(None),
12 }
13}
14
15#[derive(Debug, Deserialize)]
16pub struct Config {
17 #[serde(default)]
18 pub default: DefaultConfig,
19 #[serde(default)]
20 pub clock: ClockConfig,
21 #[serde(default)]
22 pub timer: TimerConfig,
23 #[serde(default)]
24 pub stopwatch: StopwatchConfig,
25 #[serde(default)]
26 pub countdown: CountdownConfig,
27}
28
29#[derive(Debug, Deserialize)]
30pub struct DefaultConfig {
31 #[serde(default = "default_mode")]
32 pub mode: String,
33 #[serde(default = "default_color")]
34 pub color: String,
35 #[serde(default = "default_size")]
36 pub size: u16,
37}
38
39#[derive(Debug, Deserialize)]
40pub struct ClockConfig {
41 #[serde(default = "default_true")]
42 pub show_date: bool,
43 #[serde(default = "default_true")]
44 pub show_seconds: bool,
45 #[serde(default = "default_false")]
46 pub show_millis: bool,
47 #[serde(default, deserialize_with = "deserialize_timezone")]
48 pub timezone: Option<Tz>,
49}
50
51#[derive(Debug, Deserialize)]
52pub struct TimerConfig {
53 #[serde(default = "default_timer_durations")]
54 pub durations: Vec<String>,
55 #[serde(default)]
56 pub titles: Vec<String>,
57 #[serde(default)]
58 pub repeat: bool,
59 #[serde(default = "default_true")]
60 pub show_millis: bool,
61 #[serde(default)]
62 pub start_paused: bool,
63 #[serde(default)]
64 pub auto_quit: bool,
65 #[serde(default)]
66 pub execute: Vec<String>,
67}
68
69#[derive(Debug, Deserialize)]
70pub struct StopwatchConfig {}
71
72#[derive(Debug, Deserialize)]
73pub struct CountdownConfig {
74 #[serde(default)]
75 pub time: Option<String>,
76 #[serde(default)]
77 pub title: Option<String>,
78 #[serde(default)]
79 pub show_millis: bool,
80 #[serde(default)]
81 pub continue_on_zero: bool,
82 #[serde(default)]
83 pub reverse: bool,
84}
85
86impl Default for DefaultConfig {
87 fn default() -> Self {
88 Self {
89 mode: default_mode(),
90 color: default_color(),
91 size: default_size(),
92 }
93 }
94}
95
96impl Default for ClockConfig {
97 fn default() -> Self {
98 Self {
99 show_date: default_true(),
100 show_seconds: default_true(),
101 show_millis: default_false(),
102 timezone: None,
103 }
104 }
105}
106
107impl Default for TimerConfig {
108 fn default() -> Self {
109 Self {
110 durations: default_timer_durations(),
111 titles: Vec::new(),
112 repeat: false,
113 show_millis: default_true(),
114 start_paused: false,
115 auto_quit: false,
116 execute: Vec::new(),
117 }
118 }
119}
120
121impl Default for StopwatchConfig {
122 fn default() -> Self {
123 Self {}
124 }
125}
126
127impl Default for CountdownConfig {
128 fn default() -> Self {
129 Self {
130 time: None,
131 title: None,
132 show_millis: false,
133 continue_on_zero: false,
134 reverse: false,
135 }
136 }
137}
138
139fn default_mode() -> String {
140 "clock".to_string()
141}
142
143fn default_color() -> String {
144 "green".to_string()
145}
146
147fn default_size() -> u16 {
148 1
149}
150
151fn default_true() -> bool {
152 true
153}
154
155fn default_false() -> bool {
156 false
157}
158
159fn default_timer_durations() -> Vec<String> {
160 vec!["25m".to_string(), "5m".to_string()]
161}
162
163impl Config {
164 pub fn load() -> Option<Self> {
165 let config_path = dirs::home_dir()?
167 .join(".config")
168 .join("tclock")
169 .join("config.toml");
170
171 if !config_path.exists() {
172 return None;
173 };
174
175 let content = std::fs::read_to_string(config_path).ok()?;
176 match toml::from_str(&content) {
177 Ok(config) => Some(config),
178 Err(e) => {
179 eprintln!("解析配置文件失败: {}", e);
180 None
181 }
182 }
183 }
184}