From cad0ac00e2e3fd0a5f55ce0ef3f770fbf4c933f3 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Wed, 2 Jul 2025 14:17:28 +0300 Subject: [PATCH] refactor(utils): use macros --- src/app/utils/filesize.rs | 98 ++++++------------------- src/app/utils/mod.rs | 11 ++- src/app/utils/netspeed.rs | 148 ++++++-------------------------------- src/app/utils/unit.rs | 89 +++++++++++++++++++++++ 4 files changed, 137 insertions(+), 209 deletions(-) create mode 100644 src/app/utils/unit.rs diff --git a/src/app/utils/filesize.rs b/src/app/utils/filesize.rs index 3ff11d1..96b56c8 100644 --- a/src/app/utils/filesize.rs +++ b/src/app/utils/filesize.rs @@ -1,47 +1,29 @@ +use super::unit::{Unit, UnitError}; +use crate::app::utils::unit::UnitDisplay; use std::fmt::Display; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum FileSizeError { - #[error("File size cannot be negative: {value}")] - NegativeSize { value: i64 }, - #[error("File size value is too large: {value}")] - ValueTooLarge { value: f64 }, - #[error("File size value is invalid: {reason}")] - InvalidValue { reason: String }, -} #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)] -pub struct FileSize(u64); +pub struct FileSize(Unit); impl FileSize { - pub const fn new(bytes: u64) -> Self { - Self(bytes) + pub fn new(bytes: u64) -> Self { + Self(Unit::new(bytes)) } +} - pub const fn bytes(&self) -> u64 { - self.0 - } - - pub const fn kilobytes(kb: u64) -> Self { - Self(kb * 1024) - } - - pub const fn megabytes(mb: u64) -> Self { - Self(mb * 1024 * 1024) - } - - pub const fn gigabytes(gb: u64) -> Self { - Self(gb * 1024 * 1024 * 1024) +impl Display for FileSize { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB", "PB"]; + write!(f, "{}", UnitDisplay::new(&self.0, UNITS)) } } macro_rules! impl_from_unsigned { - ($($t:ty),*) => { + ($type:ty, $($t:ty), *) => { $( - impl From<$t> for FileSize { + impl From<$t> for $type { fn from(value: $t) -> Self { - Self(value as u64) + Self(Unit::from(value)) } } )* @@ -49,61 +31,21 @@ macro_rules! impl_from_unsigned { } macro_rules! impl_try_from_signed { - ($($t:ty),*) => { + ($type:ty, $error:ty, $($t:ty), *) => { $( - impl TryFrom<$t> for FileSize { - type Error = FileSizeError; + impl TryFrom<$t> for $type { + type Error = $error; fn try_from(value: $t) -> Result { if value < 0 { - Err(FileSizeError::NegativeSize { value: value as i64 }) - } else { - Ok(Self(value as u64)) + return Err(UnitError::NegativeValue { value: value as i64 }); } + Ok(Self(Unit::try_from(value)?)) } } )* }; } -impl_from_unsigned!(u8, u16, u32, u64, usize); -impl_try_from_signed!(i8, i16, i32, i64, isize); - -impl Display for FileSize { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB", "PB"]; - const THREASHOLD: f64 = 1024.0; - - let bytes = self.0 as f64; - - if bytes < THREASHOLD { - return write!(f, "{} {}", self.0, UNITS[0]); - } - - let mut size = bytes; - let mut unit_index = 0; - - while size >= THREASHOLD && unit_index < UNITS.len() - 1 { - size /= THREASHOLD; - unit_index += 1; - } - if unit_index == 0 { - return write!(f, "{} {}", size as u64, UNITS[unit_index]); - } - write!(f, "{:.2} {}", size, UNITS[unit_index]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_file_size_display() { - assert_eq!(FileSize::new(512).to_string(), "512 B"); - assert_eq!(FileSize::new(1536).to_string(), "1.50 KB"); - assert_eq!(FileSize::new(1048576).to_string(), "1.00 MB"); - assert_eq!(FileSize::new(1073741824).to_string(), "1.00 GB"); - assert_eq!(FileSize::new(1099511627776).to_string(), "1.00 TB"); - } -} +impl_from_unsigned!(FileSize, u8, u16, u32, u64, usize); +impl_try_from_signed!(FileSize, UnitError, i8, i16, i32, i64, isize); diff --git a/src/app/utils/mod.rs b/src/app/utils/mod.rs index 45bf974..ecee000 100644 --- a/src/app/utils/mod.rs +++ b/src/app/utils/mod.rs @@ -1,5 +1,6 @@ pub mod filesize; pub mod netspeed; +pub mod unit; use filesize::FileSize; use netspeed::NetSpeed; @@ -121,7 +122,9 @@ impl Wrapper for TorrentGetField { .map(|v| format!("{:?}", v)) .unwrap_or_default(), Self::Comment => torrent.comment.clone().unwrap_or_default(), - Self::CorruptEver => FileSize::from(torrent.corrupt_ever.unwrap_or(0)).to_string(), + Self::CorruptEver => FileSize::try_from(torrent.corrupt_ever.unwrap_or(0)) + .unwrap_or_default() + .to_string(), Self::Creator => torrent.creator.clone().unwrap_or_default(), Self::DateCreated => torrent .date_created @@ -179,7 +182,7 @@ impl Wrapper for TorrentGetField { .unwrap_or_default(), Self::Group => torrent.group.clone().unwrap_or_default(), Self::HashString => torrent.hash_string.clone().unwrap_or_default(), - Self::HaveUnchecked => todo!(), + Self::HaveUnchecked => FileSize::from(torrent.have_unchecked.unwrap_or(0)).to_string(), Self::HaveValid => FileSize::from(torrent.have_valid.unwrap_or(0)).to_string(), Self::HonorsSessionLimits => torrent .honors_session_limits @@ -199,7 +202,9 @@ impl Wrapper for TorrentGetField { .map(|v| v.to_string()) .unwrap_or_default(), Self::Labels => torrent.labels.clone().unwrap_or_default().join(", "), - Self::LeftUntilDone => todo!(), + Self::LeftUntilDone => FileSize::try_from(torrent.left_until_done.unwrap_or(0)) + .unwrap_or_default() + .to_string(), Self::MagnetLink => torrent.magnet_link.clone().unwrap_or_default(), Self::ManualAnnounceTime => torrent .manual_announce_time diff --git a/src/app/utils/netspeed.rs b/src/app/utils/netspeed.rs index 0d938af..4c6a709 100644 --- a/src/app/utils/netspeed.rs +++ b/src/app/utils/netspeed.rs @@ -1,67 +1,29 @@ +use super::unit::{Unit, UnitError}; +use crate::app::utils::unit::UnitDisplay; use std::fmt::Display; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum NetSpeedError { - #[error("Network speed cannot be negative: {value}")] - NegativeSpeed { value: i64 }, - #[error("Network speed value is too large: {value}")] - ValueTooLarge { value: f64 }, - #[error("Network speed value is invalid: {reason}")] - InvalidValue { reason: String }, -} #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)] -pub struct NetSpeed(u64); +pub struct NetSpeed(Unit); impl NetSpeed { - pub const fn new(bytes_per_second: u64) -> Self { - Self(bytes_per_second) + pub fn new(bytes_per_second: u64) -> Self { + Self(Unit::new(bytes_per_second)) } +} - pub const fn bytes_per_second(&self) -> u64 { - self.0 - } - - pub const fn kilobytes_per_second(kb: u64) -> Self { - Self(kb * 1024) - } - - pub const fn megabytes_per_second(mb: u64) -> Self { - Self(mb * 1024 * 1024) - } - - pub const fn gigabytes_per_second(gb: u64) -> Self { - Self(gb * 1024 * 1024 * 1024) - } - - pub const fn from_bits_per_second(bps: u64) -> Self { - NetSpeed(bps / 8) - } - - pub const fn to_bits_per_second(&self) -> u64 { - self.0 * 8 - } - - pub const fn from_kilobits_per_second(kbps: u64) -> Self { - NetSpeed(kbps * 1000 / 8) - } - - pub const fn from_megabits_per_second(mbps: u64) -> Self { - NetSpeed(mbps * 1_000_000 / 8) - } - - pub const fn from_gigabits_per_second(gbps: u64) -> Self { - NetSpeed(gbps * 1_000_000_000 / 8) +impl Display for NetSpeed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + const UNITS: &[&str] = &["B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s"]; + write!(f, "{}", UnitDisplay::new(&self.0, UNITS)) } } macro_rules! impl_from_unsigned { - ($($t:ty),*) => { + ($type:ty, $($t:ty), *) => { $( - impl From<$t> for NetSpeed { + impl From<$t> for $type { fn from(value: $t) -> Self { - Self(value as u64) + Self(Unit::from(value)) } } )* @@ -69,91 +31,21 @@ macro_rules! impl_from_unsigned { } macro_rules! impl_try_from_signed { - ($($t:ty),*) => { + ($type:ty, $error:ty, $($t:ty), *) => { $( - impl TryFrom<$t> for NetSpeed { - type Error = NetSpeedError; + impl TryFrom<$t> for $type { + type Error = $error; fn try_from(value: $t) -> Result { if value < 0 { - Err(NetSpeedError::NegativeSpeed { value: value as i64 }) - } else { - Ok(Self(value as u64)) + return Err(UnitError::NegativeValue { value: value as i64 }); } + Ok(Self(Unit::try_from(value)?)) } } )* }; } -impl_from_unsigned!(u8, u16, u32, u64, usize); -impl_try_from_signed!(i8, i16, i32, i64, isize); - -impl Display for NetSpeed { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - const UNITS: &[&str] = &["B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s"]; - const THREASHOLD: f64 = 1024.0; - - let bytes_per_second = self.0 as f64; - - if bytes_per_second < THREASHOLD { - return write!(f, "{} {}", self.0, UNITS[0]); - } - - let mut size = bytes_per_second; - let mut unit_index = 0; - - while size >= THREASHOLD && unit_index < UNITS.len() - 1 { - size /= THREASHOLD; - unit_index += 1; - } - if unit_index == 0 { - return write!(f, "{} {}", size as u64, UNITS[unit_index]); - } - write!(f, "{:.2} {}", size, UNITS[unit_index]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_net_speed_display() { - assert_eq!(NetSpeed::new(512).to_string(), "512 B/s"); - assert_eq!(NetSpeed::new(1536).to_string(), "1.50 KB/s"); - assert_eq!(NetSpeed::new(1048576).to_string(), "1.00 MB/s"); - assert_eq!(NetSpeed::new(1073741824).to_string(), "1.00 GB/s"); - assert_eq!(NetSpeed::new(1099511627776).to_string(), "1.00 TB/s"); - } - - #[test] - fn test_bits_conversion() { - let speed = NetSpeed::from_bits_per_second(8000); - assert_eq!(speed.bytes_per_second(), 1000); - assert_eq!(speed.to_bits_per_second(), 8000); - } - - #[test] - fn test_network_units() { - // 1 Mbps = 125,000 bytes per second - let speed = NetSpeed::from_megabits_per_second(1); - assert_eq!(speed.bytes_per_second(), 125_000); - - // 1 Gbps = 125,000,000 bytes per second - let speed = NetSpeed::from_gigabits_per_second(1); - assert_eq!(speed.bytes_per_second(), 125_000_000); - } - - #[test] - fn test_try_from_i64() { - assert_eq!( - NetSpeed::try_from(1000i64).unwrap().bytes_per_second(), - 1000 - ); - assert_eq!( - NetSpeed::try_from(-100i64), - Err(NetSpeedError::NegativeSpeed { value: -100 }) - ); - } -} +impl_from_unsigned!(NetSpeed, u8, u16, u32, u64, usize); +impl_try_from_signed!(NetSpeed, UnitError, i8, i16, i32, i64, isize); diff --git a/src/app/utils/unit.rs b/src/app/utils/unit.rs new file mode 100644 index 0000000..3b46163 --- /dev/null +++ b/src/app/utils/unit.rs @@ -0,0 +1,89 @@ +use std::fmt::Display; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum UnitError { + #[error("Value cannot be negative: {value}")] + NegativeValue { value: i64 }, + #[error("Value is too large: {value}")] + ValueTooLarge { value: f64 }, + #[error("Value is invalid: {reason}")] + InvalidValue { reason: String }, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)] +pub struct Unit(u64); + +impl Unit { + pub const fn new(value: u64) -> Self { + Self(value) + } + + pub const fn value(&self) -> u64 { + self.0 + } +} + +pub struct UnitDisplay<'a> { + unit: &'a Unit, + units: &'a [&'a str], +} + +impl<'a> UnitDisplay<'a> { + pub fn new(unit: &'a Unit, units: &'a [&'a str]) -> Self { + Self { unit, units } + } +} + +impl<'a> Display for UnitDisplay<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + const THREASHOLD: f64 = 1024.0; + + let value = self.unit.0 as f64; + + if value < THREASHOLD { + return write!(f, "{} {}", self.unit.0, self.units[0]); + } + + let mut size = value; + let mut unit_index = 0; + + while size >= THREASHOLD && unit_index < self.units.len() - 1 { + size /= THREASHOLD; + unit_index += 1; + } + write!(f, "{:.2} {}", size, self.units[unit_index]) + } +} + +macro_rules! impl_from_unsigned { + ($type:ty, $($t:ty), *) => { + $( + impl From<$t> for $type { + fn from(value: $t) -> Self { + Self(value as u64) + } + } + )* + }; +} + +macro_rules! impl_try_from_signed { + ($type:ty, $error:ty, $($t:ty), *) => { + $( + impl TryFrom<$t> for $type { + type Error = $error; + + fn try_from(value: $t) -> Result { + if value < 0 { + return Err(UnitError::NegativeValue { value: value as i64 }); + } + Ok(Self(value as u64)) + } + } + )* + }; +} + +impl_from_unsigned!(Unit, u8, u16, u32, u64, usize); +impl_try_from_signed!(Unit, UnitError, i8, i16, i32, i64, isize);