1use crate::filenames::saturn_config;
2use anyhow::Result;
3use chrono::Duration;
4use fancy_duration::FancyDuration;
5use gcal::ClientParameters;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub enum DBType {
10 #[default]
11 UnixFile,
12 Google,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Config {
17 db_type: DBType,
18 access_token: Option<String>,
19 access_token_expires_at: Option<chrono::NaiveDateTime>,
20 refresh_token: Option<String>,
21 refresh_token_expires_at: Option<chrono::NaiveDateTime>,
22 client_info: Option<(String, String)>,
23 redirect_url: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 sync_duration: Option<FancyDuration<Duration>>,
26 default_duration: Option<FancyDuration<Duration>>,
27 use_24h_time: Option<bool>,
28 query_window: Option<FancyDuration<Duration>>,
29 calendar_id: String,
30}
31
32impl From<Config> for ClientParameters {
33 fn from(value: Config) -> Self {
34 Self {
35 client_id: value.client_id().unwrap_or_default(),
36 client_secret: value.client_secret().unwrap_or_default(),
37 redirect_url: value.redirect_url(),
38 access_key: value.access_token(),
39 expires_at: value.access_token_expires_at(),
40 refresh_token: value.refresh_token(),
41 refresh_token_expires_at: value.refresh_token_expires_at(),
42 }
43 }
44}
45
46impl Default for Config {
47 fn default() -> Self {
48 Self {
49 query_window: Some(FancyDuration::new(chrono::TimeDelta::try_days(30).unwrap_or_default())),
50 use_24h_time: Some(false),
51 db_type: DBType::UnixFile,
52 access_token: None,
53 access_token_expires_at: None,
54 refresh_token: None,
55 refresh_token_expires_at: None,
56 redirect_url: None,
57 client_info: None,
58 sync_duration: None,
59 default_duration: None,
60 calendar_id: "primary".to_string(),
61 }
62 }
63}
64
65impl Config {
66 pub fn load(filename: Option<std::path::PathBuf>) -> Result<Self> {
67 let path = filename.unwrap_or(saturn_config());
68 let mut io = std::fs::OpenOptions::new();
69 io.read(true);
70
71 match io.open(path) {
72 Ok(io) => Ok(serde_yaml::from_reader(io)?),
73 Err(_) => Ok(Self::default()),
74 }
75 }
76
77 pub fn save(&self, filename: Option<std::path::PathBuf>) -> Result<()> {
78 let path = filename.unwrap_or(saturn_config());
79 let mut io = std::fs::OpenOptions::new();
80 io.write(true);
81 io.truncate(true);
82 io.create(true);
83 let io = io.open(path)?;
84
85 Ok(serde_yaml::to_writer(io, self)?)
86 }
87
88 pub fn set_calendar_id(&mut self, calendar_id: String) {
89 self.calendar_id = calendar_id;
90 }
91
92 pub fn set_access_token(&mut self, access_token: Option<String>) {
93 self.access_token = access_token;
94 }
95
96 pub fn set_access_token_expires_at(&mut self, expires_at: Option<chrono::NaiveDateTime>) {
97 self.access_token_expires_at = expires_at;
98 }
99
100 pub fn set_refresh_token(&mut self, refresh_token: Option<String>) {
101 self.refresh_token = refresh_token;
102 }
103
104 pub fn set_refresh_token_expires_at(&mut self, expires_at: Option<chrono::NaiveDateTime>) {
105 self.refresh_token_expires_at = expires_at;
106 }
107
108 pub fn access_token(&self) -> Option<String> {
109 self.access_token.clone()
110 }
111
112 pub fn access_token_expires_at(&self) -> Option<chrono::NaiveDateTime> {
113 self.access_token_expires_at
114 }
115
116 pub fn refresh_token(&self) -> Option<String> {
117 self.refresh_token.clone()
118 }
119
120 pub fn refresh_token_expires_at(&self) -> Option<chrono::NaiveDateTime> {
121 self.refresh_token_expires_at
122 }
123
124 pub fn set_redirect_url(&mut self, redirect_url: Option<String>) {
125 self.redirect_url = redirect_url;
126 }
127
128 pub fn set_default_duration(&mut self, default_duration: Option<FancyDuration<Duration>>) {
129 self.default_duration = default_duration;
130 }
131
132 pub fn default_duration(&self) -> FancyDuration<Duration> {
133 if let Some(duration) = &self.default_duration {
134 duration.clone()
135 } else {
136 FancyDuration(chrono::TimeDelta::try_minutes(15).unwrap_or_default())
137 }
138 }
139
140 pub fn calendar_id(&self) -> String {
141 self.calendar_id.clone()
142 }
143
144 pub fn redirect_url(&self) -> Option<String> {
145 self.redirect_url.clone()
146 }
147
148 pub fn set_db_type(&mut self, typ: DBType) {
149 self.db_type = typ;
150 }
151
152 pub fn db_type(&self) -> DBType {
153 self.db_type.clone()
154 }
155
156 pub fn use_24h_time(&self) -> bool {
157 self.use_24h_time.unwrap_or_default()
158 }
159
160 pub fn set_use_24h_time(&mut self, use_24h_time: bool) {
161 self.use_24h_time = Some(use_24h_time)
162 }
163
164 pub fn query_window(&self) -> chrono::Duration {
165 self.query_window
166 .clone()
167 .map_or_else(|| chrono::TimeDelta::try_days(30).unwrap_or_default(), |x| x.duration())
168 }
169
170 pub fn set_query_window(&mut self, window: chrono::Duration) {
171 self.query_window = Some(FancyDuration::new(window))
172 }
173
174 pub fn set_client_info(&mut self, client_id: String, client_secret: String) {
175 self.client_info = Some((client_id, client_secret))
176 }
177
178 pub fn has_client(&self) -> bool {
179 self.client_info.is_some()
180 }
181
182 pub fn client_id(&self) -> Option<String> {
183 self.client_info.clone().map(|s| s.0)
184 }
185
186 pub fn client_secret(&self) -> Option<String> {
187 self.client_info.clone().map(|s| s.1)
188 }
189}