use std::fmt::Display;
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct SessionStats {
pub start_time: Instant,
pub total_bits_down: u128,
pub total_bits_up: u128,
}
impl Default for SessionStats {
fn default() -> Self {
Self {
start_time: Instant::now(),
total_bits_down: 0,
total_bits_up: 0,
}
}
}
impl SessionStats {
pub fn new() -> Self {
Self::default()
}
pub fn with_start_time(start_time: Instant) -> Self {
Self {
start_time,
total_bits_down: 0,
total_bits_up: 0,
}
}
pub fn add_download(&mut self, bits: u128) {
self.total_bits_down += bits;
}
pub fn add_upload(&mut self, bits: u128) {
self.total_bits_up += bits;
}
pub fn duration(&self) -> std::time::Duration {
self.start_time.elapsed()
}
pub fn format_summary(&self) -> String {
let duration = self.duration();
let seconds = duration.as_secs();
let down_mib = (self.total_bits_down as f64) / (8.0 * 1024.0 * 1024.0);
let up_mib = (self.total_bits_up as f64) / (8.0 * 1024.0 * 1024.0);
if seconds < 60 {
format!(
"Session: ↓ {:.2} MiB | ↑ {:.2} MiB | {}s",
down_mib, up_mib, seconds
)
} else {
let minutes = seconds / 60;
format!(
"Session: ↓ {:.2} MiB | ↑ {:.2} MiB | {}m",
down_mib, up_mib, minutes
)
}
}
pub fn total_bytes_down(&self) -> u128 {
self.total_bits_down / 8
}
pub fn total_bytes_up(&self) -> u128 {
self.total_bits_up / 8
}
}
impl Display for SessionStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.format_summary())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_session_stats_default() {
let stats = SessionStats::default();
assert_eq!(stats.total_bits_down, 0);
assert_eq!(stats.total_bits_up, 0);
}
#[test]
fn test_session_stats_new() {
let stats = SessionStats::new();
assert_eq!(stats.total_bits_down, 0);
assert_eq!(stats.total_bits_up, 0);
assert!(stats.duration().as_secs() < 1);
}
#[test]
fn test_add_download() {
let mut stats = SessionStats::new();
stats.add_download(1000);
assert_eq!(stats.total_bits_down, 1000);
assert_eq!(stats.total_bytes_down(), 125);
}
#[test]
fn test_add_upload() {
let mut stats = SessionStats::new();
stats.add_upload(500);
assert_eq!(stats.total_bits_up, 500);
assert_eq!(stats.total_bytes_up(), 62);
}
#[test]
fn test_format_summary_empty() {
let stats = SessionStats::new();
let summary = stats.format_summary();
assert!(summary.contains("Session:"));
assert!(summary.contains("↓ 0.00 MiB"));
assert!(summary.contains("↑ 0.00 MiB"));
assert!(summary.contains("s")); }
#[test]
fn test_format_summary_with_data() {
let mut stats = SessionStats::new();
stats.add_download(8 * 1024 * 1024);
stats.add_upload(4 * 1024 * 1024);
let summary = stats.format_summary();
assert!(summary.contains("↓ 1.00 MiB"));
assert!(summary.contains("↑ 0.50 MiB"));
}
#[test]
fn test_session_stats_display() {
let stats = SessionStats::new();
let display = format!("{}", stats);
assert!(display.contains("Session:"));
}
}