webserver_colin_ugo/utils/
stats.rs

1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3use serde::Serialize;
4
5/// Structure représentant les statistiques du serveur
6#[derive(Debug, Clone, Serialize)]
7pub struct ServerStats {
8    /// Nombre total de requêtes servies
9    pub total_requests: u64,
10    /// Nombre de requêtes par code d'erreur
11    pub error_counts: HashMap<String, u64>,
12}
13
14impl Default for ServerStats {
15    fn default() -> Self {
16        Self {
17            total_requests: 0,
18            error_counts: HashMap::new(),
19        }
20    }
21}
22
23/// Gestionnaire de statistiques thread-safe pour le serveur web
24pub struct StatsManager {
25    stats: Arc<Mutex<ServerStats>>,
26}
27
28impl StatsManager {
29    /// Crée une nouvelle instance de StatsManager
30    pub fn new() -> Self {
31        Self {
32            stats: Arc::new(Mutex::new(ServerStats::default())),
33        }
34    }
35
36    /// Récupère une copie des statistiques actuelles
37    pub fn get_stats(&self) -> ServerStats {
38        self.stats.lock().unwrap().clone()
39    }
40
41    /// Incrémente le compteur total de requêtes
42    pub fn increment_request_count(&self) {
43        let mut stats = self.stats.lock().unwrap();
44        stats.total_requests += 1;
45    }
46
47    /// Incrémente le compteur pour un code d'erreur spécifique
48    ///
49    /// # Arguments
50    ///
51    /// * `status_code` - Le code de statut HTTP (ex: "404 Not Found")
52    pub fn increment_error_count(&self, status_code: &str) {
53        if !status_code.starts_with("4") && !status_code.starts_with("5") {
54            return;
55        }
56
57        let mut stats = self.stats.lock().unwrap();
58        let counter = stats.error_counts.entry(status_code.to_string()).or_insert(0);
59        *counter += 1;
60    }
61
62    /// Génère une représentation JSON des statistiques actuelles
63    pub fn to_json(&self) -> Result<String, serde_json::Error> {
64        let stats = self.get_stats();
65        serde_json::to_string_pretty(&stats)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_stats_manager() {
75        let stats_manager = StatsManager::new();
76        
77        // Tester l'incrémentation du nombre total de requêtes
78        stats_manager.increment_request_count();
79        stats_manager.increment_request_count();
80        let stats = stats_manager.get_stats();
81        assert_eq!(stats.total_requests, 2);
82        
83        // Tester l'incrémentation des compteurs d'erreurs
84        stats_manager.increment_error_count("404 Not Found");
85        stats_manager.increment_error_count("404 Not Found");
86        stats_manager.increment_error_count("500 Internal Server Error");
87        
88        let stats = stats_manager.get_stats();
89        assert_eq!(*stats.error_counts.get("404 Not Found").unwrap(), 2);
90        assert_eq!(*stats.error_counts.get("500 Internal Server Error").unwrap(), 1);
91    }
92    
93    #[test]
94    fn test_error_counts_only_for_errors() {
95        let stats_manager = StatsManager::new();
96        
97        stats_manager.increment_error_count("200 OK");
98        
99        let stats = stats_manager.get_stats();
100        assert!(stats.error_counts.get("200 OK").is_none());
101    }
102    
103    #[test]
104    fn test_to_json() {
105        let stats_manager = StatsManager::new();
106        stats_manager.increment_request_count();
107        stats_manager.increment_error_count("404 Not Found");
108        
109        let json = stats_manager.to_json().unwrap();
110        assert!(json.contains("total_requests"));
111        assert!(json.contains("error_counts"));
112        assert!(json.contains("404 Not Found"));
113    }
114}