1use jiff::Timestamp;
8use serde::{Deserialize, Serialize};
9use std::time::Duration;
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24pub struct UpdateConfig {
25 pub auto_update_enabled: bool,
31
32 pub auto_update_check_interval: Duration,
37}
38
39impl Default for UpdateConfig {
40 fn default() -> Self {
41 Self {
42 auto_update_enabled: true,
43 auto_update_check_interval: Duration::from_secs(86400), }
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
67pub struct UpdateInfo {
68 pub version: String,
72
73 pub release_date: Timestamp,
75
76 pub notes: String,
80
81 pub download_url: String,
85
86 pub signature_url: String,
91
92 pub arch: String,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
112pub struct UpdateHistory {
113 pub last_check: Timestamp,
115
116 pub current_version: String,
118
119 pub pending_update: Option<UpdateInfo>,
125
126 pub backup_versions: Vec<String>,
131
132 pub check_history: Vec<UpdateCheckEntry>,
137}
138
139impl Default for UpdateHistory {
140 fn default() -> Self {
141 Self {
142 last_check: Timestamp::now(),
143 current_version: String::new(),
144 pending_update: None,
145 backup_versions: Vec::new(),
146 check_history: Vec::new(),
147 }
148 }
149}
150
151impl UpdateHistory {
152 pub fn add_check_entry(&mut self, entry: UpdateCheckEntry) {
159 self.check_history.push(entry);
160 if self.check_history.len() > 10 {
162 self.check_history.remove(0);
163 }
164 }
165
166 pub fn add_backup_version(&mut self, version: String, max_backups: usize) {
174 self.backup_versions.push(version);
175 if self.backup_versions.len() > max_backups {
177 self.backup_versions.remove(0);
178 }
179 }
180
181 pub fn latest_backup(&self) -> Option<&String> {
185 self.backup_versions.last()
186 }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193pub struct UpdateCheckEntry {
194 pub timestamp: Timestamp,
196
197 pub result: UpdateCheckResult,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
206pub enum UpdateCheckResult {
207 UpToDate,
211
212 UpdateAvailable { version: String, notified: bool },
217
218 CheckFailed { error: String },
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_update_config_default() {
230 let config = UpdateConfig::default();
231 assert!(config.auto_update_enabled, "Default should be enabled");
232 assert_eq!(
233 config.auto_update_check_interval,
234 Duration::from_secs(86400),
235 "Default should be 24 hours"
236 );
237 }
238
239 #[test]
240 fn test_update_config_serialize() {
241 let config = UpdateConfig {
242 auto_update_enabled: false,
243 auto_update_check_interval: Duration::from_secs(3600), };
245
246 let serialized = serde_json::to_string(&config).unwrap();
247 let deserialized: UpdateConfig = serde_json::from_str(&serialized).unwrap();
248
249 assert_eq!(config, deserialized);
250 }
251
252 #[test]
253 fn test_update_info_serialization() {
254 let info = UpdateInfo {
255 version: "1.2.3".to_string(),
256 release_date: Timestamp::now(),
257 notes: "Test release".to_string(),
258 download_url: "https://example.com/binary".to_string(),
259 signature_url: "https://example.com/binary.sig".to_string(),
260 arch: "x86_64".to_string(),
261 };
262
263 let serialized = serde_json::to_string(&info).unwrap();
264 let deserialized: UpdateInfo = serde_json::from_str(&serialized).unwrap();
265
266 assert_eq!(info.version, deserialized.version);
267 assert_eq!(info.download_url, deserialized.download_url);
268 assert_eq!(info.signature_url, deserialized.signature_url);
269 assert_eq!(info.arch, deserialized.arch);
270 }
271
272 #[test]
273 fn test_update_history_default() {
274 let history = UpdateHistory::default();
275 assert_eq!(history.current_version, String::new());
276 assert!(history.pending_update.is_none());
277 assert!(history.backup_versions.is_empty());
278 assert!(history.check_history.is_empty());
279 }
280
281 #[test]
282 fn test_update_history_add_entry() {
283 let mut history = UpdateHistory::default();
284 let entry = UpdateCheckEntry {
285 timestamp: Timestamp::now(),
286 result: UpdateCheckResult::UpToDate,
287 };
288
289 history.add_check_entry(entry);
290 assert_eq!(history.check_history.len(), 1);
291 }
292
293 #[test]
294 fn test_update_history_limit_entries() {
295 let mut history = UpdateHistory::default();
296
297 for _i in 0..15 {
299 history.add_check_entry(UpdateCheckEntry {
300 timestamp: Timestamp::now(),
301 result: UpdateCheckResult::UpToDate,
302 });
303 }
304
305 assert_eq!(
306 history.check_history.len(),
307 10,
308 "Should keep only last 10 entries"
309 );
310 }
311
312 #[test]
313 fn test_update_history_add_backup() {
314 let mut history = UpdateHistory::default();
315 let max_backups = 3;
316
317 for i in 0..5 {
319 history.add_backup_version(format!("1.0.{}", i), max_backups);
320 }
321
322 assert_eq!(
323 history.backup_versions.len(),
324 3,
325 "Should keep only 3 backups"
326 );
327 assert_eq!(
328 history.backup_versions,
329 vec![
330 "1.0.2".to_string(),
331 "1.0.3".to_string(),
332 "1.0.4".to_string()
333 ]
334 );
335 }
336
337 #[test]
338 fn test_update_history_latest_backup() {
339 let mut history = UpdateHistory::default();
340 history.add_backup_version("1.0.0".to_string(), 3);
341 history.add_backup_version("1.0.1".to_string(), 3);
342
343 assert_eq!(history.latest_backup(), Some(&"1.0.1".to_string()));
344 }
345
346 #[test]
347 fn test_update_check_entry_serialization() {
348 let entry = UpdateCheckEntry {
349 timestamp: Timestamp::now(),
350 result: UpdateCheckResult::UpdateAvailable {
351 version: "1.1.0".to_string(),
352 notified: false,
353 },
354 };
355
356 let serialized = serde_json::to_string(&entry).unwrap();
357 let deserialized: UpdateCheckEntry = serde_json::from_str(&serialized).unwrap();
358
359 match deserialized.result {
360 UpdateCheckResult::UpdateAvailable { version, notified } => {
361 assert_eq!(version, "1.1.0");
362 assert!(!notified);
363 }
364 _ => panic!("Expected UpdateAvailable variant"),
365 }
366 }
367
368 #[test]
369 fn test_update_check_result_variants() {
370 let up_to_date = UpdateCheckResult::UpToDate;
371 let update_available = UpdateCheckResult::UpdateAvailable {
372 version: "1.2.0".to_string(),
373 notified: true,
374 };
375 let check_failed = UpdateCheckResult::CheckFailed {
376 error: "Network error".to_string(),
377 };
378
379 assert_ne!(up_to_date, update_available);
381 assert_ne!(up_to_date, check_failed);
382 assert_ne!(update_available, check_failed);
383
384 for result in [up_to_date, update_available, check_failed] {
386 let serialized = serde_json::to_string(&result).unwrap();
387 let deserialized: UpdateCheckResult = serde_json::from_str(&serialized).unwrap();
388 assert_eq!(result, deserialized);
389 }
390 }
391}