use std::fmt::Display;
use std::ops::{Add, AddAssign, Div};
#[derive(Default, Debug, Clone, Copy)]
pub struct Speed {
pub output: u128,
pub input: u128,
}
impl Speed {
pub const fn new(input: u128, output: u128) -> Self {
Self { input, output }
}
pub fn to_string_input(&self) -> String {
format_size(self.input)
}
pub fn to_string_output(&self) -> String {
format_size(self.output)
}
}
impl Add for Speed {
type Output = Speed;
fn add(self, rhs: Self) -> Self::Output {
Speed {
input: self.input + rhs.input,
output: self.output + rhs.output,
}
}
}
impl AddAssign for Speed {
fn add_assign(&mut self, rhs: Self) {
self.input += rhs.input;
self.output += rhs.output;
}
}
impl Div<u128> for Speed {
type Output = Speed;
fn div(self, rhs: u128) -> Self::Output {
Speed {
input: self.input / rhs,
output: self.output / rhs,
}
}
}
impl Display for Speed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"↓ {} | ↑ {}",
format_size(self.input),
format_size(self.output)
)
}
}
pub fn format_size(bits: u128) -> String {
let bytes = bits as f64 / 8.0;
const UNIT_1024: f64 = 1024.0;
if bytes < UNIT_1024 {
return format!("{:.2} B/s", bytes);
}
let kib = bytes / UNIT_1024;
if kib < UNIT_1024 {
return format!("{:.2} KiB/s", kib);
}
let mib = kib / UNIT_1024;
if mib < UNIT_1024 {
return format!("{:.2} MiB/s", mib);
}
let gib = mib / UNIT_1024;
format!("{:.2} GiB/s", gib)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_speed_default() {
let speed = Speed::default();
assert_eq!(speed.input, 0);
assert_eq!(speed.output, 0);
}
#[test]
fn test_speed_new() {
let speed = Speed::new(1000, 2000);
assert_eq!(speed.input, 1000);
assert_eq!(speed.output, 2000);
}
#[test]
fn test_speed_add() {
let a = Speed::new(100, 200);
let b = Speed::new(50, 75);
let sum = a + b;
assert_eq!(sum.input, 150);
assert_eq!(sum.output, 275);
}
#[test]
fn test_speed_add_assign() {
let mut speed = Speed::new(100, 200);
speed += Speed::new(50, 75);
assert_eq!(speed.input, 150);
assert_eq!(speed.output, 275);
}
#[test]
fn test_speed_div() {
let speed = Speed::new(1000, 2000);
let result = speed / 2;
assert_eq!(result.input, 500);
assert_eq!(result.output, 1000);
}
#[test]
fn test_speed_display() {
let mib_2_bits = 16_777_216;
let mib_1_bits = 8_388_608;
let speed = Speed::new(mib_2_bits, mib_1_bits);
let display = format!("{}", speed);
assert!(display.contains("2.00 MiB/s"));
assert!(display.contains("1.00 MiB/s"));
assert!(display.contains("↓"));
assert!(display.contains("↑"));
}
#[test]
fn test_format_size_bytes() {
assert_eq!(format_size(8), "1.00 B/s");
assert_eq!(format_size(8000), "1000.00 B/s");
}
#[test]
fn test_format_size_kib() {
assert_eq!(format_size(8192), "1.00 KiB/s");
assert_eq!(format_size(16384), "2.00 KiB/s");
}
#[test]
fn test_format_size_mib() {
assert_eq!(format_size(8_388_608), "1.00 MiB/s");
}
#[test]
fn test_format_size_gib() {
assert_eq!(format_size(8_589_934_592), "1.00 GiB/s");
}
#[test]
fn test_speed_to_string_input() {
let speed = Speed::new(16_777_216, 0);
assert_eq!(speed.to_string_input(), "2.00 MiB/s");
}
#[test]
fn test_speed_to_string_output() {
let speed = Speed::new(0, 8_388_608);
assert_eq!(speed.to_string_output(), "1.00 MiB/s");
}
}