Skip to main content

lmn_core/histogram/
status_code.rs

1use std::collections::HashMap;
2
3// ── StatusCodeHistogram ───────────────────────────────────────────────────────
4
5/// Tracks the frequency of HTTP status codes and connection errors across requests.
6pub struct StatusCodeHistogram {
7    counts: HashMap<u16, u64>,
8    error_count: u64,
9}
10
11impl StatusCodeHistogram {
12    /// Creates a new empty histogram.
13    pub fn new() -> Self {
14        Self {
15            counts: HashMap::new(),
16            error_count: 0,
17        }
18    }
19
20    /// Records a single request outcome.
21    ///
22    /// `None` status code represents a connection error (no HTTP response received).
23    pub fn record(&mut self, status_code: Option<u16>) {
24        match status_code {
25            Some(code) => *self.counts.entry(code).or_insert(0) += 1,
26            None => self.error_count += 1,
27        }
28    }
29
30    /// Returns the map of HTTP status code to count.
31    pub fn counts(&self) -> &HashMap<u16, u64> {
32        &self.counts
33    }
34
35    /// Returns the number of connection errors (requests with no HTTP response).
36    pub fn error_count(&self) -> u64 {
37        self.error_count
38    }
39
40    /// Returns the total number of recorded requests (status codes + errors).
41    pub fn total(&self) -> u64 {
42        self.counts.values().sum::<u64>() + self.error_count
43    }
44}
45
46impl Default for StatusCodeHistogram {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52// ── Tests ─────────────────────────────────────────────────────────────────────
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn record_200_increments_count() {
60        let mut h = StatusCodeHistogram::new();
61        h.record(Some(200));
62        assert_eq!(h.counts()[&200], 1);
63    }
64
65    #[test]
66    fn record_none_increments_error_count() {
67        let mut h = StatusCodeHistogram::new();
68        h.record(None);
69        h.record(None);
70        assert_eq!(h.error_count(), 2);
71    }
72
73    #[test]
74    fn total_sums_all_codes_and_errors() {
75        let mut h = StatusCodeHistogram::new();
76        h.record(Some(200));
77        h.record(Some(200));
78        h.record(Some(404));
79        h.record(None);
80        assert_eq!(h.total(), 4);
81    }
82
83    #[test]
84    fn multiple_codes_tracked_independently() {
85        let mut h = StatusCodeHistogram::new();
86        h.record(Some(200));
87        h.record(Some(201));
88        h.record(Some(404));
89        h.record(Some(503));
90        assert_eq!(h.counts()[&200], 1);
91        assert_eq!(h.counts()[&201], 1);
92        assert_eq!(h.counts()[&404], 1);
93        assert_eq!(h.counts()[&503], 1);
94    }
95}