1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::Path;
4use std::sync::{Arc, RwLock};
5
6use crate::selector::server_stat::{ServerStat, ServerStatSnapshot};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ServerStatFile {
31 pub version: String,
33 pub saved_at: u64,
35 pub servers: Vec<ServerStatSnapshot>,
37}
38
39pub struct ServerStatMan {
40 stats: RwLock<HashMap<String, Arc<ServerStat>>>,
41}
42
43impl ServerStatMan {
44 pub fn new() -> Self {
45 Self {
46 stats: RwLock::new(HashMap::new()),
47 }
48 }
49
50 pub fn get_or_create(&self, host: &str) -> Arc<ServerStat> {
51 let mut map = self.stats.write().unwrap();
52 if let Some(stat) = map.get(host) {
53 Arc::clone(stat)
54 } else {
55 let stat = Arc::new(ServerStat::new(host));
56 map.insert(host.to_string(), Arc::clone(&stat));
57 stat
58 }
59 }
60
61 pub fn find_stat(&self, host: &str) -> Option<Arc<ServerStat>> {
62 let map = self.stats.read().unwrap();
63 map.get(host).cloned()
64 }
65
66 pub fn update(&self, host: &str, dl_speed: u64, is_multi: bool) {
67 let stat = self.get_or_create(host);
68 stat.update_speed(dl_speed, is_multi);
69 }
70
71 pub fn get_all_stats(&self) -> Vec<Arc<ServerStat>> {
72 let map = self.stats.read().unwrap();
73 map.values().cloned().collect()
74 }
75
76 pub fn remove(&self, host: &str) {
77 let mut map = self.stats.write().unwrap();
78 map.remove(host);
79 }
80
81 pub fn count(&self) -> usize {
82 let map = self.stats.read().unwrap();
83 map.len()
84 }
85
86 pub fn hosts(&self) -> Vec<String> {
87 let map = self.stats.read().unwrap();
88 map.keys().cloned().collect()
89 }
90
91 pub fn mark_failure(&self, host: &str, error_code: u16) {
96 let mut map = self.stats.write().unwrap();
97 if let Some(stat_arc) = map.get(host) {
98 let inner: &ServerStat = stat_arc;
100 let mut updated = inner.clone();
101 updated.set_failure_info(error_code);
102 map.insert(host.to_string(), Arc::new(updated));
103 }
104 }
105
106 pub fn save_to_file(&self, path: &Path) -> Result<usize, String> {
135 let map = self.stats.read().unwrap();
136
137 let servers: Vec<ServerStatSnapshot> =
138 map.values().map(|stat| stat.to_snapshot()).collect();
139
140 let file_content = ServerStatFile {
141 version: "1.0".to_string(),
142 saved_at: std::time::SystemTime::now()
143 .duration_since(std::time::UNIX_EPOCH)
144 .unwrap_or_default()
145 .as_secs(),
146 servers,
147 };
148
149 let json = serde_json::to_string_pretty(&file_content)
150 .map_err(|e| format!("Failed to serialize server stats: {}", e))?;
151
152 std::fs::write(path, json)
153 .map_err(|e| format!("Failed to write server stat file: {}", e))?;
154
155 Ok(file_content.servers.len())
156 }
157
158 pub fn load_from_file(&self, path: &Path) -> Result<usize, String> {
189 if !path.exists() {
190 return Ok(0); }
192
193 let content = std::fs::read_to_string(path)
194 .map_err(|e| format!("Failed to read server stat file: {}", e))?;
195
196 let file_content: ServerStatFile = serde_json::from_str(&content)
197 .map_err(|e| format!("Invalid server stat file format: {}", e))?;
198
199 let count = file_content.servers.len();
200 let mut map = self.stats.write().unwrap();
201
202 for snapshot in file_content.servers {
203 let stat = Arc::new(ServerStat::from_snapshot(&snapshot));
204 map.insert(snapshot.host, stat);
205 }
206
207 Ok(count)
208 }
209
210 pub async fn save_to_file_async(&self, path: &Path) -> Result<usize, String> {
224 let path = path.to_path_buf();
225 let snapshots: Vec<ServerStatSnapshot> = {
226 let map = self.stats.read().unwrap();
227 map.values().map(|stat| stat.to_snapshot()).collect()
228 };
229
230 let file_content = ServerStatFile {
231 version: "1.0".to_string(),
232 saved_at: std::time::SystemTime::now()
233 .duration_since(std::time::UNIX_EPOCH)
234 .unwrap_or_default()
235 .as_secs(),
236 servers: snapshots,
237 };
238
239 let json = serde_json::to_string_pretty(&file_content)
240 .map_err(|e| format!("Failed to serialize server stats: {}", e))?;
241
242 tokio::fs::write(&path, json)
243 .await
244 .map_err(|e| format!("Failed to write server stat file: {}", e))?;
245
246 Ok(file_content.servers.len())
247 }
248
249 pub async fn load_from_file_async(&self, path: &Path) -> Result<usize, String> {
263 if !path.exists() {
264 return Ok(0);
265 }
266
267 let content = tokio::fs::read_to_string(path)
268 .await
269 .map_err(|e| format!("Failed to read server stat file: {}", e))?;
270
271 let file_content: ServerStatFile = serde_json::from_str(&content)
272 .map_err(|e| format!("Invalid server stat file format: {}", e))?;
273
274 let count = file_content.servers.len();
275 let mut map = self.stats.write().unwrap();
276 for snapshot in file_content.servers {
277 let stat = Arc::new(ServerStat::from_snapshot(&snapshot));
278 map.insert(snapshot.host, stat);
279 }
280
281 Ok(count)
282 }
283}
284
285impl Default for ServerStatMan {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn test_creation_and_count() {
297 let man = ServerStatMan::new();
298 assert_eq!(man.count(), 0);
299 }
300
301 #[test]
302 fn test_get_or_create_new_host() {
303 let man = ServerStatMan::new();
304 let stat = man.get_or_create("example.com");
305 assert_eq!(stat.host, "example.com");
306 assert_eq!(man.count(), 1);
307 }
308
309 #[test]
310 fn test_get_or_create_returns_same_instance() {
311 let man = ServerStatMan::new();
312 let s1 = man.get_or_create("example.com");
313 let s2 = man.get_or_create("example.com");
314 assert!(Arc::ptr_eq(&s1, &s2));
315 assert_eq!(man.count(), 1);
316 }
317
318 #[test]
319 fn test_find_existing() {
320 let man = ServerStatMan::new();
321 man.get_or_create("example.com");
322 assert!(man.find_stat("example.com").is_some());
323 assert!(man.find_stat("nonexistent").is_none());
324 }
325
326 #[test]
327 fn test_update_creates_if_needed() {
328 let man = ServerStatMan::new();
329 man.update("fast.server", 5000, false);
330 assert_eq!(man.count(), 1);
331
332 let stat = man.find_stat("fast.server").unwrap();
333 assert_eq!(stat.get_download_speed(), 5000);
334 }
335
336 #[test]
337 fn test_remove() {
338 let man = ServerStatMan::new();
339 man.get_or_create("a.com");
340 man.get_or_create("b.com");
341 assert_eq!(man.count(), 2);
342
343 man.remove("a.com");
344 assert_eq!(man.count(), 1);
345 assert!(man.find_stat("a.com").is_none());
346 }
347
348 #[test]
349 fn test_multiple_hosts_independent() {
350 let man = ServerStatMan::new();
351 man.update("fast.com", 10000, true);
352 man.update("slow.com", 100, false);
353
354 let fast = man.find_stat("fast.com").unwrap();
355 let slow = man.find_stat("slow.com").unwrap();
356
357 assert_ne!(fast.get_avg_speed(), slow.get_avg_speed());
358 assert!(fast.get_avg_speed() > slow.get_avg_speed());
359 }
360
361 #[test]
362 fn test_hosts_list() {
363 let man = ServerStatMan::new();
364 man.get_or_create("alpha.com");
365 man.get_or_create("beta.com");
366 let hosts = man.hosts();
367 assert_eq!(hosts.len(), 2);
368 assert!(hosts.contains(&"alpha.com".to_string()));
369 assert!(hosts.contains(&"beta.com".to_string()));
370 }
371
372 #[test]
377 fn test_mark_failure_updates_stats() {
378 let man = ServerStatMan::new();
379 man.get_or_create("failing.host");
380
381 man.mark_failure("failing.host", 500);
383
384 let stat = man.find_stat("failing.host").unwrap();
385 assert_eq!(stat.get_consecutive_failures(), 1);
386 assert!(stat.get_last_error_time() > 0);
387 assert_eq!(stat.get_last_error_code(), 500);
388 }
389
390 #[test]
391 fn test_mark_failure_multiple_times() {
392 let man = ServerStatMan::new();
393 man.get_or_create("repeated.failures");
394
395 for i in 0..5u16 {
396 man.mark_failure("repeated.failures", i);
397 }
398
399 let stat = man.find_stat("repeated.failures").unwrap();
400 assert_eq!(stat.get_consecutive_failures(), 5);
401 assert!(
402 !stat.is_available(),
403 "Should be unavailable after 5 failures"
404 );
405 }
406
407 #[test]
408 fn test_mark_failure_nonexistent_host() {
409 let man = ServerStatMan::new();
410 man.mark_failure("nonexistent.host", 404);
412 assert_eq!(man.count(), 0); }
414
415 #[test]
420 fn test_save_to_file_basic() {
421 let man = ServerStatMan::new();
422 man.update("fast.mirror.com", 10000, false);
423 man.update("slow.mirror.com", 100, true);
424
425 let temp_file = std::env::temp_dir().join("test_server_stat_save.json");
426 let saved = man.save_to_file(&temp_file).expect("Save should succeed");
427
428 assert_eq!(saved, 2, "Should save 2 servers");
429
430 let content = std::fs::read_to_string(&temp_file).expect("Should read file");
432 let parsed: serde_json::Value =
433 serde_json::from_str(&content).expect("Should be valid JSON");
434 assert!(parsed.get("version").is_some());
435 assert!(parsed.get("saved_at").is_some());
436 assert!(parsed.get("servers").is_some());
437
438 let _ = std::fs::remove_file(&temp_file);
439 }
440
441 #[test]
442 fn test_load_from_file_basic() {
443 let man = ServerStatMan::new();
444 man.update("load.test.com", 5000, false);
445 man.mark_failure("load.test.com", 500);
446
447 let temp_file = std::env::temp_dir().join("test_server_stat_load.json");
448 let _ = man.save_to_file(&temp_file);
449
450 let man2 = ServerStatMan::new();
452 let loaded = man2
453 .load_from_file(&temp_file)
454 .expect("Load should succeed");
455
456 assert_eq!(loaded, 1, "Should load 1 server");
457
458 let stat = man2
459 .find_stat("load.test.com")
460 .expect("Should find loaded server");
461 assert!(stat.get_avg_speed() > 0);
462 assert_eq!(stat.get_consecutive_failures(), 1);
463
464 let _ = std::fs::remove_file(&temp_file);
465 }
466
467 #[test]
468 fn test_save_load_roundtrip() {
469 let man = ServerStatMan::new();
470 man.update("mirror1.com", 10000, false);
471 man.update("mirror2.com", 5000, true);
472 man.update("mirror3.com", 2000, false);
473 man.mark_failure("mirror3.com", 503);
474 man.mark_failure("mirror3.com", 503);
475
476 let temp_file = std::env::temp_dir().join("test_server_stat_roundtrip.json");
477 let saved = man.save_to_file(&temp_file).unwrap();
478 assert_eq!(saved, 3);
479
480 let man2 = ServerStatMan::new();
481 let loaded = man2.load_from_file(&temp_file).unwrap();
482 assert_eq!(loaded, 3);
483
484 let s1 = man2.find_stat("mirror1.com").unwrap();
486 let s2 = man2.find_stat("mirror2.com").unwrap();
487 let s3 = man2.find_stat("mirror3.com").unwrap();
488
489 assert!(s1.get_single_avg_speed() > 0);
490 assert!(s2.get_multi_avg_speed() > 0);
491 assert_eq!(s3.get_consecutive_failures(), 2);
492 assert_eq!(s3.get_last_error_code(), 503);
493
494 let _ = std::fs::remove_file(&temp_file);
495 }
496
497 #[test]
498 fn test_load_nonexistent_file() {
499 let man = ServerStatMan::new();
500 let nonexistent = std::path::Path::new("/tmp/nonexistent_server_stat_12345.json");
501
502 let loaded = man
503 .load_from_file(nonexistent)
504 .expect("Should return Ok(0) for nonexistent file");
505 assert_eq!(loaded, 0);
506 assert_eq!(man.count(), 0);
507 }
508
509 #[test]
510 fn test_save_empty_manager() {
511 let man = ServerStatMan::new();
512 let temp_file = std::env::temp_dir().join("test_server_stat_empty.json");
513
514 let saved = man
515 .save_to_file(&temp_file)
516 .expect("Should save empty manager");
517 assert_eq!(saved, 0);
518
519 let content = std::fs::read_to_string(&temp_file).expect("Should read file");
521 let parsed: serde_json::Value =
522 serde_json::from_str(&content).expect("Should be valid JSON");
523 let servers = parsed.get("servers").unwrap().as_array().unwrap();
524 assert!(servers.is_empty());
525
526 let _ = std::fs::remove_file(&temp_file);
527 }
528
529 #[test]
530 fn test_load_merges_with_existing() {
531 let man = ServerStatMan::new();
532 man.update("existing.com", 1000, false);
533
534 let temp_file = std::env::temp_dir().join("test_server_stat_merge.json");
536 let man2 = ServerStatMan::new();
537 man2.update("fromfile.com", 5000, false);
538 let _ = man2.save_to_file(&temp_file);
539
540 let loaded = man.load_from_file(&temp_file).unwrap();
542 assert_eq!(loaded, 1);
543
544 assert_eq!(man.count(), 2);
546 assert!(man.find_stat("existing.com").is_some());
547 assert!(man.find_stat("fromfile.com").is_some());
548
549 let _ = std::fs::remove_file(&temp_file);
550 }
551
552 #[tokio::test]
553 async fn test_async_save_and_load() {
554 let man = ServerStatMan::new();
555 man.update("async.test.com", 8000, false);
556 man.update("async2.test.com", 4000, true);
557
558 let temp_file = std::env::temp_dir().join("test_server_stat_async.json");
559
560 let saved = man
562 .save_to_file_async(&temp_file)
563 .await
564 .expect("Async save should succeed");
565 assert_eq!(saved, 2);
566
567 let man2 = ServerStatMan::new();
569 let loaded = man2
570 .load_from_file_async(&temp_file)
571 .await
572 .expect("Async load should succeed");
573 assert_eq!(loaded, 2);
574
575 let stat = man2.find_stat("async.test.com").unwrap();
576 assert!(stat.get_avg_speed() > 0);
577
578 let _ = std::fs::remove_file(&temp_file);
579 }
580
581 #[test]
582 fn test_file_format_structure() {
583 let man = ServerStatMan::new();
584 man.update("format.test", 12345, false);
585
586 let temp_file = std::env::temp_dir().join("test_server_stat_format.json");
587 let _ = man.save_to_file(&temp_file);
588
589 let content = std::fs::read_to_string(&temp_file).unwrap();
590 let parsed: ServerStatFile = serde_json::from_str(&content).unwrap();
591
592 assert_eq!(parsed.version, "1.0");
593 assert!(parsed.saved_at > 0);
594 assert_eq!(parsed.servers.len(), 1);
595 assert_eq!(parsed.servers[0].host, "format.test");
596 assert_eq!(parsed.servers[0].download_speed, 12345);
597
598 let _ = std::fs::remove_file(&temp_file);
599 }
600}