mirror of
https://github.com/kristoferssolo/traxor.git
synced 2025-10-21 20:10:35 +00:00
refactor(utils): update NetSpeed and FileSize structs
This commit is contained in:
parent
9efeec3890
commit
9baf60c98b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1628,6 +1628,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"crossterm 0.29.0",
|
||||
"ratatui",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
||||
@ -14,3 +14,4 @@ anyhow = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
thiserror = "2.0"
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
use std::{collections::HashSet, path::Path};
|
||||
|
||||
|
||||
use transmission_rpc::types::{Torrent, TorrentAction, TorrentStatus};
|
||||
|
||||
use super::{types::Selected, Torrents};
|
||||
use std::{collections::HashSet, path::Path};
|
||||
use transmission_rpc::types::{Torrent, TorrentAction, TorrentStatus};
|
||||
|
||||
impl Torrents {
|
||||
pub async fn toggle(&mut self, ids: Selected) -> anyhow::Result<()> {
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
mod tab;
|
||||
mod torrent;
|
||||
pub mod utils;
|
||||
|
||||
use ratatui::widgets::TableState;
|
||||
pub mod action;
|
||||
mod command;
|
||||
mod tab;
|
||||
mod torrent;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
pub use {tab::Tab, torrent::Torrents};
|
||||
|
||||
use self::types::Selected;
|
||||
pub use self::{tab::Tab, torrent::Torrents};
|
||||
use ratatui::widgets::TableState;
|
||||
use types::Selected;
|
||||
|
||||
/// Main Application.
|
||||
/// TODO: write description
|
||||
#[derive(Debug)]
|
||||
pub struct App<'a> {
|
||||
pub running: bool,
|
||||
@ -149,7 +147,10 @@ impl<'a> App<'a> {
|
||||
fn selected(&self, highlighted: bool) -> Selected {
|
||||
let torrents = &self.torrents.torrents;
|
||||
if self.torrents.selected.is_empty() || highlighted {
|
||||
let selected_id = self.state.selected().and_then(|idx| torrents.get(idx).and_then(|torrent| torrent.id));
|
||||
let selected_id = self
|
||||
.state
|
||||
.selected()
|
||||
.and_then(|idx| torrents.get(idx).and_then(|torrent| torrent.id));
|
||||
if let Some(id) = selected_id {
|
||||
return Selected::Current(id);
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use transmission_rpc::types::TorrentGetField;
|
||||
|
||||
/// Available tabs.
|
||||
/// TODO: write description
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Tab {
|
||||
#[default]
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
use std::{collections::HashSet, fmt::Debug};
|
||||
|
||||
use anyhow::Result;
|
||||
use std::{collections::HashSet, fmt::Debug};
|
||||
use transmission_rpc::{
|
||||
types::{Torrent, TorrentGetField},
|
||||
TransClient,
|
||||
};
|
||||
|
||||
use url::Url;
|
||||
|
||||
/// List of torrents.
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use transmission_rpc::types::Id;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
||||
@ -1,24 +1,109 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
pub struct FileSize(i64);
|
||||
#[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 },
|
||||
}
|
||||
|
||||
impl From<i64> for FileSize {
|
||||
fn from(bytes: i64) -> Self {
|
||||
FileSize(bytes)
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct FileSize(u64);
|
||||
|
||||
impl FileSize {
|
||||
pub const fn new(bytes: u64) -> Self {
|
||||
Self(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 fmt::Display for FileSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let bytes = self.0;
|
||||
if bytes < 1024 {
|
||||
write!(f, "{} B", bytes)
|
||||
} else if bytes < 1024 * 1024 {
|
||||
write!(f, "{:.2} KB", bytes as f64 / 1024.0)
|
||||
} else if bytes < 1024 * 1024 * 1024 {
|
||||
write!(f, "{:.2} MB", bytes as f64 / (1024.0 * 1024.0))
|
||||
} else {
|
||||
write!(f, "{:.2} GB", bytes as f64 / (1024.0 * 1024.0 * 1024.0))
|
||||
macro_rules! impl_from_unsigned {
|
||||
($($t:ty),*) => {
|
||||
$(
|
||||
impl From<$t> for FileSize {
|
||||
fn from(value: $t) -> Self {
|
||||
Self(value as u64)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_try_from_signed {
|
||||
($($t:ty),*) => {
|
||||
$(
|
||||
impl TryFrom<$t> for FileSize {
|
||||
type Error = FileSizeError;
|
||||
|
||||
fn try_from(value: $t) -> Result<Self, Self::Error> {
|
||||
if value < 0 {
|
||||
Err(FileSizeError::NegativeSize { value: value as i64 })
|
||||
} else {
|
||||
Ok(Self(value as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use transmission_rpc::types::{ErrorType, Torrent, TorrentGetField, TorrentStatus};
|
||||
|
||||
pub mod filesize;
|
||||
pub mod netspeed;
|
||||
|
||||
use crate::app::utils::filesize::FileSize;
|
||||
use crate::app::utils::netspeed::NetSpeed;
|
||||
use filesize::FileSize;
|
||||
use netspeed::NetSpeed;
|
||||
use transmission_rpc::types::{
|
||||
ErrorType, IdleMode, RatioMode, Torrent, TorrentGetField, TorrentStatus,
|
||||
};
|
||||
|
||||
pub trait Wrapper {
|
||||
fn title(&self) -> String {
|
||||
@ -23,113 +24,136 @@ pub trait Wrapper {
|
||||
impl Wrapper for TorrentGetField {
|
||||
fn title(&self) -> String {
|
||||
match self {
|
||||
Self::ActivityDate => "Activity Date".to_string(),
|
||||
Self::AddedDate => "Added Date".to_string(),
|
||||
Self::Availability => todo!(),
|
||||
Self::BandwidthPriority => todo!(),
|
||||
Self::Comment => todo!(),
|
||||
Self::CorruptEver => todo!(),
|
||||
Self::Creator => todo!(),
|
||||
Self::DateCreated => todo!(),
|
||||
Self::DesiredAvailable => todo!(),
|
||||
Self::DoneDate => "Done Date".to_string(),
|
||||
Self::DownloadDir => "Path".to_string(),
|
||||
Self::DownloadLimit => todo!(),
|
||||
Self::DownloadLimited => todo!(),
|
||||
Self::DownloadedEver => todo!(),
|
||||
Self::EditDate => "Edit Date".to_string(),
|
||||
Self::Error => "Error Type".to_string(),
|
||||
Self::ErrorString => "Error String".to_string(),
|
||||
Self::Eta => "ETA".to_string(),
|
||||
Self::EtaIdle => todo!(),
|
||||
Self::FileCount => todo!(),
|
||||
Self::FileStats => "File Stats".to_string(),
|
||||
Self::Files => "Files".to_string(),
|
||||
Self::Group => todo!(),
|
||||
Self::HashString => "Hash String".to_string(),
|
||||
Self::HaveUnchecked => todo!(),
|
||||
Self::HaveValid => todo!(),
|
||||
Self::HonorsSessionLimits => todo!(),
|
||||
Self::Id => "Id".to_string(),
|
||||
Self::IsFinished => "Finished".to_string(),
|
||||
Self::IsPrivate => "Private".to_string(),
|
||||
Self::IsStalled => "Stalled".to_string(),
|
||||
Self::Labels => "Labels".to_string(),
|
||||
Self::LeftUntilDone => "Left Until Done".to_string(),
|
||||
Self::MagnetLink => todo!(),
|
||||
Self::ManualAnnounceTime => todo!(),
|
||||
Self::MaxConnectedPeers => todo!(),
|
||||
Self::MetadataPercentComplete => "Metadata Percent Complete".to_string(),
|
||||
Self::Name => "Name".to_string(),
|
||||
Self::PeerLimit => todo!(),
|
||||
Self::Peers => todo!(),
|
||||
Self::PeersConnected => "Connected".to_string(),
|
||||
Self::PeersFrom => todo!(),
|
||||
Self::PeersGettingFromUs => "Peers".to_string(),
|
||||
Self::PeersSendingToUs => "Seeds".to_string(),
|
||||
Self::PercentComplete => todo!(),
|
||||
Self::PercentDone => "%".to_string(),
|
||||
Self::PieceCount => todo!(),
|
||||
Self::PieceSize => todo!(),
|
||||
Self::Pieces => todo!(),
|
||||
Self::PrimaryMimeType => todo!(),
|
||||
Self::Priorities => "Priorities".to_string(),
|
||||
Self::QueuePosition => "Queue".to_string(),
|
||||
Self::RateDownload => "Download Speed".to_string(),
|
||||
Self::RateUpload => "Upload Speed".to_string(),
|
||||
Self::RecheckProgress => "Progress".to_string(),
|
||||
Self::SecondsDownloading => todo!(),
|
||||
Self::SecondsSeeding => "Seconds Seeding".to_string(),
|
||||
Self::SeedIdleLimit => todo!(),
|
||||
Self::SeedIdleMode => todo!(),
|
||||
Self::SeedRatioLimit => "Seed Ratio Limit".to_string(),
|
||||
Self::SeedRatioMode => "Seed Ratio Mode".to_string(),
|
||||
Self::SequentialDownload => todo!(),
|
||||
Self::SizeWhenDone => "Size".to_string(),
|
||||
Self::StartDate => todo!(),
|
||||
Self::Status => "Status".to_string(),
|
||||
Self::TorrentFile => "Torrent File".to_string(),
|
||||
Self::TotalSize => "Total Size".to_string(),
|
||||
Self::TrackerList => todo!(),
|
||||
Self::TrackerStats => todo!(),
|
||||
Self::Trackers => "Trackers".to_string(),
|
||||
Self::UploadLimit => todo!(),
|
||||
Self::UploadLimited => todo!(),
|
||||
Self::UploadRatio => "Ratio".to_string(),
|
||||
Self::UploadedEver => "Uploaded".to_string(),
|
||||
Self::Wanted => "Wanted".to_string(),
|
||||
Self::Webseeds => todo!(),
|
||||
Self::WebseedsSendingToUs => "Webseeds Sending to Us".to_string(),
|
||||
Self::ActivityDate => "Activity Date",
|
||||
Self::AddedDate => "Added Date",
|
||||
Self::Availability => "Availability",
|
||||
Self::BandwidthPriority => "Bandwidth Priority",
|
||||
Self::Comment => "Comment",
|
||||
Self::CorruptEver => "Corrupt Ever",
|
||||
Self::Creator => "Creator",
|
||||
Self::DateCreated => "Date Created",
|
||||
Self::DesiredAvailable => "Desired Available",
|
||||
Self::DoneDate => "Done Date",
|
||||
Self::DownloadDir => "Path",
|
||||
Self::DownloadLimit => "Download Limit",
|
||||
Self::DownloadLimited => "Download Limited",
|
||||
Self::DownloadedEver => "Downloaded Ever",
|
||||
Self::EditDate => "Edit Date",
|
||||
Self::Error => "Error Type",
|
||||
Self::ErrorString => "Error String",
|
||||
Self::Eta => "ETA",
|
||||
Self::EtaIdle => "ETA Idle",
|
||||
Self::FileCount => "File Count",
|
||||
Self::FileStats => "File Stats",
|
||||
Self::Files => "Files",
|
||||
Self::Group => "Group",
|
||||
Self::HashString => "Hash String",
|
||||
Self::HaveUnchecked => "Have Unchecked",
|
||||
Self::HaveValid => "Have Valid",
|
||||
Self::HonorsSessionLimits => "Honors Session Limits",
|
||||
Self::Id => "Id",
|
||||
Self::IsFinished => "Finished",
|
||||
Self::IsPrivate => "Private",
|
||||
Self::IsStalled => "Stalled",
|
||||
Self::Labels => "Labels",
|
||||
Self::LeftUntilDone => "Left Until Done",
|
||||
Self::MagnetLink => "Magnet Link",
|
||||
Self::ManualAnnounceTime => "Manual Announce Time",
|
||||
Self::MaxConnectedPeers => "Max Connected Peers",
|
||||
Self::MetadataPercentComplete => "Metadata Percent Complete",
|
||||
Self::Name => "Name",
|
||||
Self::PeerLimit => "Peer Limit",
|
||||
Self::Peers => "Peers",
|
||||
Self::PeersConnected => "Connected",
|
||||
Self::PeersFrom => "Peers From",
|
||||
Self::PeersGettingFromUs => "Peers",
|
||||
Self::PeersSendingToUs => "Seeds",
|
||||
Self::PercentComplete => "Percent Complete",
|
||||
Self::PercentDone => "%",
|
||||
Self::PieceCount => "Piece Count",
|
||||
Self::PieceSize => "Piece Size",
|
||||
Self::Pieces => "Pieces",
|
||||
Self::PrimaryMimeType => "Primary Mime Type",
|
||||
Self::Priorities => "Priorities",
|
||||
Self::QueuePosition => "Queue",
|
||||
Self::RateDownload => "Download Speed",
|
||||
Self::RateUpload => "Upload Speed",
|
||||
Self::RecheckProgress => "Progress",
|
||||
Self::SecondsDownloading => "Seconds Downloading",
|
||||
Self::SecondsSeeding => "Seconds Seeding",
|
||||
Self::SeedIdleLimit => "Seed Idle Limit",
|
||||
Self::SeedIdleMode => "Seed Idle Mode",
|
||||
Self::SeedRatioLimit => "Seed Ratio Limit",
|
||||
Self::SeedRatioMode => "Seed Ratio Mode",
|
||||
Self::SequentialDownload => "Sequential Download",
|
||||
Self::SizeWhenDone => "Size",
|
||||
Self::StartDate => "Start Date",
|
||||
Self::Status => "Status",
|
||||
Self::TorrentFile => "Torrent File",
|
||||
Self::TotalSize => "Total Size",
|
||||
Self::TrackerList => "Tracker List",
|
||||
Self::TrackerStats => "Tracker Stats",
|
||||
Self::Trackers => "Trackers",
|
||||
Self::UploadLimit => "Upload Limit",
|
||||
Self::UploadLimited => "Upload Limited",
|
||||
Self::UploadRatio => "Ratio",
|
||||
Self::UploadedEver => "Uploaded",
|
||||
Self::Wanted => "Wanted",
|
||||
Self::Webseeds => "Webseeds",
|
||||
Self::WebseedsSendingToUs => "Webseeds Sending to Us",
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn value(&self, torrent: &Torrent) -> String {
|
||||
match self {
|
||||
Self::ActivityDate => torrent.activity_date.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::AddedDate => torrent.added_date.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::Availability => todo!(),
|
||||
Self::BandwidthPriority => todo!(),
|
||||
Self::Comment => todo!(),
|
||||
Self::CorruptEver => todo!(),
|
||||
Self::Creator => todo!(),
|
||||
Self::DateCreated => todo!(),
|
||||
Self::DesiredAvailable => todo!(),
|
||||
Self::DoneDate => torrent.done_date.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::DownloadDir => torrent.download_dir.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::DownloadLimit => todo!(),
|
||||
Self::DownloadLimited => todo!(),
|
||||
Self::DownloadedEver => todo!(),
|
||||
Self::EditDate => torrent.edit_date.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::ActivityDate => torrent
|
||||
.activity_date
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::AddedDate => torrent
|
||||
.added_date
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::Availability => "N/A".to_string(),
|
||||
Self::BandwidthPriority => torrent
|
||||
.bandwidth_priority
|
||||
.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::Creator => torrent.creator.clone().unwrap_or_default(),
|
||||
Self::DateCreated => torrent
|
||||
.date_created
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::DesiredAvailable => {
|
||||
FileSize::from(torrent.desired_available.unwrap_or(0)).to_string()
|
||||
}
|
||||
Self::DoneDate => torrent.done_date.map(|v| v.to_string()).unwrap_or_default(),
|
||||
Self::DownloadDir => torrent.download_dir.clone().unwrap_or_default().to_string(),
|
||||
Self::DownloadLimit => NetSpeed::from(torrent.download_limit.unwrap_or(0)).to_string(),
|
||||
Self::DownloadLimited => torrent
|
||||
.download_limited
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::DownloadedEver => {
|
||||
FileSize::from(torrent.downloaded_ever.unwrap_or(0)).to_string()
|
||||
}
|
||||
Self::EditDate => torrent.edit_date.map(|v| v.to_string()).unwrap_or_default(),
|
||||
Self::Error => match torrent.error {
|
||||
Some(error) => match error {
|
||||
ErrorType::Ok => "Ok".to_string(),
|
||||
ErrorType::LocalError => "LocalError".to_string(),
|
||||
ErrorType::TrackerError => "TrackerError".to_string(),
|
||||
ErrorType::TrackerWarning => "TrackerWarning".to_string(),
|
||||
ErrorType::Ok => "Ok",
|
||||
ErrorType::LocalError => "LocalError",
|
||||
ErrorType::TrackerError => "TrackerError",
|
||||
ErrorType::TrackerWarning => "TrackerWarning",
|
||||
},
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::ErrorString => torrent.error_string.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
None => "N/A",
|
||||
}
|
||||
.to_string(),
|
||||
Self::ErrorString => torrent.error_string.clone().unwrap_or_default(),
|
||||
Self::Eta => match torrent.eta {
|
||||
Some(eta) => match eta {
|
||||
-1 => "".to_string(),
|
||||
@ -138,184 +162,306 @@ impl Wrapper for TorrentGetField {
|
||||
},
|
||||
None => "".to_string(),
|
||||
},
|
||||
Self::EtaIdle => todo!(),
|
||||
Self::FileCount => todo!(),
|
||||
Self::FileStats => match &torrent.file_stats {
|
||||
Some(file_stats) => file_stats
|
||||
.iter()
|
||||
.map(|x| format!("{:?}", x.priority))
|
||||
.collect(),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::Files => match &torrent.files {
|
||||
Some(files) => files.iter().map(|x| x.name.to_owned()).collect(),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::Group => todo!(),
|
||||
Self::HashString => torrent.hash_string.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::EtaIdle => torrent.eta_idle.map(|v| v.to_string()).unwrap_or_default(),
|
||||
Self::FileCount => torrent
|
||||
.file_count
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::FileStats => torrent
|
||||
.file_stats
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::Files => torrent
|
||||
.files
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::Group => torrent.group.clone().unwrap_or_default(),
|
||||
Self::HashString => torrent.hash_string.clone().unwrap_or_default(),
|
||||
Self::HaveUnchecked => todo!(),
|
||||
Self::HaveValid => todo!(),
|
||||
Self::HonorsSessionLimits => todo!(),
|
||||
Self::Id => torrent.id.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::IsFinished => torrent.is_finished.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::IsPrivate => torrent.is_private.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::IsStalled => torrent.is_stalled.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::Labels => torrent.labels.as_ref().map_or_else(|| "N/A".to_string(), |v| v.join(" ")),
|
||||
Self::LeftUntilDone => FileSize::from(torrent.left_until_done.unwrap_or(0)).to_string(),
|
||||
Self::MagnetLink => todo!(),
|
||||
Self::ManualAnnounceTime => todo!(),
|
||||
Self::MaxConnectedPeers => todo!(),
|
||||
Self::MetadataPercentComplete => torrent.metadata_percent_complete.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::Name => torrent.name.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::PeerLimit => todo!(),
|
||||
Self::Peers => todo!(),
|
||||
Self::PeersConnected => torrent.peers_connected.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::PeersFrom => todo!(),
|
||||
Self::PeersGettingFromUs => torrent.peers_getting_from_us.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::PeersSendingToUs => torrent.peers_sending_to_us.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::PercentComplete => todo!(),
|
||||
Self::PercentDone => match torrent.percent_done {
|
||||
Some(percent_done) => format!("{:.0}", percent_done * 100.0),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::PieceCount => todo!(),
|
||||
Self::PieceSize => todo!(),
|
||||
Self::Pieces => todo!(),
|
||||
Self::PrimaryMimeType => todo!(),
|
||||
Self::Priorities => match &torrent.priorities {
|
||||
Some(priorities) => priorities.iter().map(|x| format!("{:?}", x)).collect(),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::QueuePosition => "N/A".to_string(),
|
||||
Self::RateDownload => NetSpeed::from(torrent.rate_download.unwrap_or(0)).to_string(),
|
||||
Self::RateUpload => NetSpeed::from(torrent.rate_upload.unwrap_or(0)).to_string(),
|
||||
Self::RecheckProgress => torrent.recheck_progress.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::SecondsDownloading => todo!(),
|
||||
Self::SecondsSeeding => torrent.seconds_seeding.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::SeedIdleLimit => todo!(),
|
||||
Self::SeedIdleMode => todo!(),
|
||||
Self::SeedRatioLimit => torrent.seed_ratio_limit.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::SeedRatioMode => "N/A".to_string(),
|
||||
Self::SequentialDownload => todo!(),
|
||||
Self::SizeWhenDone => FileSize::from(torrent.size_when_done.unwrap_or(0)).to_string(),
|
||||
Self::StartDate => todo!(),
|
||||
Self::HaveValid => FileSize::from(torrent.have_valid.unwrap_or(0)).to_string(),
|
||||
Self::HonorsSessionLimits => torrent
|
||||
.honors_session_limits
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::Id => torrent.id.map(|v| v.to_string()).unwrap_or_default(),
|
||||
Self::IsFinished => torrent
|
||||
.is_finished
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::IsPrivate => torrent
|
||||
.is_private
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::IsStalled => torrent
|
||||
.is_stalled
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::Labels => torrent.labels.clone().unwrap_or_default().join(", "),
|
||||
Self::LeftUntilDone => todo!(),
|
||||
Self::MagnetLink => torrent.magnet_link.clone().unwrap_or_default(),
|
||||
Self::ManualAnnounceTime => torrent
|
||||
.manual_announce_time
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::MaxConnectedPeers => torrent
|
||||
.max_connected_peers
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::MetadataPercentComplete => torrent
|
||||
.metadata_percent_complete
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_default(),
|
||||
Self::Name => torrent.name.clone().unwrap_or_default(),
|
||||
Self::PeerLimit => torrent
|
||||
.peer_limit
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::Peers => torrent
|
||||
.peers
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::PeersConnected => torrent
|
||||
.peers_connected
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::PeersFrom => torrent
|
||||
.peers_from
|
||||
.as_ref()
|
||||
.map(|p| {
|
||||
format!(
|
||||
"d:{} u:{} i:{} t:{}",
|
||||
p.from_dht, p.from_incoming, p.from_lpd, p.from_tracker
|
||||
)
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
Self::PeersGettingFromUs => torrent
|
||||
.peers_getting_from_us
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::PeersSendingToUs => torrent
|
||||
.peers_sending_to_us
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::PercentComplete => torrent
|
||||
.percent_complete
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_default(),
|
||||
Self::PercentDone => torrent
|
||||
.percent_done
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_default(),
|
||||
Self::PieceCount => torrent
|
||||
.piece_count
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::PieceSize => FileSize::from(torrent.piece_size.unwrap_or(0)).to_string(),
|
||||
Self::Pieces => torrent
|
||||
.pieces
|
||||
.as_ref()
|
||||
.map(|p| format!("{} bytes", p.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::PrimaryMimeType => torrent.primary_mime_type.clone().unwrap_or_default(),
|
||||
Self::Priorities => torrent
|
||||
.priorities
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::QueuePosition => torrent
|
||||
.queue_position
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::RateDownload => NetSpeed::try_from(torrent.rate_download.unwrap_or(0))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::RateUpload => NetSpeed::try_from(torrent.rate_upload.unwrap_or(0))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::RecheckProgress => torrent
|
||||
.recheck_progress
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_default(),
|
||||
Self::SecondsDownloading => torrent
|
||||
.seconds_downloading
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::SecondsSeeding => torrent
|
||||
.seconds_seeding
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::SeedIdleLimit => torrent
|
||||
.seed_idle_limit
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::SeedIdleMode => torrent
|
||||
.seed_idle_mode
|
||||
.map(|v| match v {
|
||||
IdleMode::Global => "Global",
|
||||
IdleMode::Single => "Single",
|
||||
IdleMode::Unlimited => "Unlimited",
|
||||
})
|
||||
.unwrap_or("N/A")
|
||||
.to_string(),
|
||||
Self::SeedRatioLimit => torrent
|
||||
.seed_ratio_limit
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_default(),
|
||||
Self::SeedRatioMode => torrent
|
||||
.seed_ratio_mode
|
||||
.map(|v| match v {
|
||||
RatioMode::Global => "Global",
|
||||
RatioMode::Single => "Single",
|
||||
RatioMode::Unlimited => "Unlimited",
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::SequentialDownload => torrent
|
||||
.sequential_download
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::SizeWhenDone => FileSize::try_from(torrent.size_when_done.unwrap_or(0))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::StartDate => torrent
|
||||
.start_date
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::Status => match torrent.status {
|
||||
Some(status) => match status {
|
||||
TorrentStatus::Stopped => "Stopped".to_string(),
|
||||
TorrentStatus::Seeding => "Seeding".to_string(),
|
||||
TorrentStatus::Verifying => "Verifying".to_string(),
|
||||
TorrentStatus::Downloading => "Downloading".to_string(),
|
||||
TorrentStatus::QueuedToSeed => "QueuedToSeed".to_string(),
|
||||
TorrentStatus::QueuedToVerify => "QueuedToVerify".to_string(),
|
||||
TorrentStatus::QueuedToDownload => "QueuedToDownload".to_string(),
|
||||
TorrentStatus::Stopped => "Stopped",
|
||||
TorrentStatus::Seeding => "Seeding",
|
||||
TorrentStatus::Verifying => "Verifying",
|
||||
TorrentStatus::Downloading => "Downloading",
|
||||
TorrentStatus::QueuedToSeed => "QueuedToSeed",
|
||||
TorrentStatus::QueuedToVerify => "QueuedToVerify",
|
||||
TorrentStatus::QueuedToDownload => "QueuedToDownload",
|
||||
},
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::TorrentFile => torrent.torrent_file.as_ref().map_or_else(|| "N/A".to_string(), |v| v.to_string()),
|
||||
Self::TotalSize => FileSize::from(torrent.total_size.unwrap_or(0)).to_string(),
|
||||
Self::TrackerList => todo!(),
|
||||
Self::TrackerStats => todo!(),
|
||||
Self::Trackers => match &torrent.trackers {
|
||||
Some(trackers) => trackers.iter().map(|x| x.announce.to_string()).collect(),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::UploadLimit => todo!(),
|
||||
Self::UploadLimited => todo!(),
|
||||
Self::UploadRatio => match torrent.upload_ratio {
|
||||
Some(upload_ratio) => format!("{:.2}", upload_ratio),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::UploadedEver => FileSize::from(torrent.uploaded_ever.unwrap_or(0)).to_string(),
|
||||
Self::Wanted => match &torrent.wanted {
|
||||
Some(wanted) => wanted.iter().map(|x| x.to_string()).collect(),
|
||||
None => "N/A".to_string(),
|
||||
},
|
||||
Self::Webseeds => todo!(),
|
||||
Self::WebseedsSendingToUs => "N/A".to_string(),
|
||||
None => "N/A",
|
||||
}
|
||||
.to_string(),
|
||||
Self::TorrentFile => torrent.torrent_file.clone().unwrap_or_default(),
|
||||
Self::TotalSize => FileSize::try_from(torrent.total_size.unwrap_or(0))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::TrackerList => torrent.tracker_list.clone().unwrap_or_default(),
|
||||
Self::TrackerStats => torrent
|
||||
.tracker_stats
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::Trackers => torrent
|
||||
.trackers
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::UploadLimit => NetSpeed::try_from(torrent.upload_limit.unwrap_or(0))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::UploadLimited => torrent
|
||||
.upload_limited
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
Self::UploadRatio => torrent
|
||||
.upload_ratio
|
||||
.map(|v| format!("{:.2}", v))
|
||||
.unwrap_or_default(),
|
||||
Self::UploadedEver => FileSize::try_from(torrent.uploaded_ever.unwrap_or(0))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
Self::Wanted => torrent
|
||||
.wanted
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v.len()))
|
||||
.unwrap_or_default(),
|
||||
Self::Webseeds => torrent.webseeds.clone().unwrap_or_default().join(", "),
|
||||
Self::WebseedsSendingToUs => torrent
|
||||
.webseeds_sending_to_us
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> u16 {
|
||||
match self {
|
||||
Self::ActivityDate => 10,
|
||||
Self::AddedDate => 10,
|
||||
Self::Availability => todo!(),
|
||||
Self::BandwidthPriority => todo!(),
|
||||
Self::Comment => todo!(),
|
||||
Self::CorruptEver => todo!(),
|
||||
Self::Creator => todo!(),
|
||||
Self::DateCreated => todo!(),
|
||||
Self::DesiredAvailable => todo!(),
|
||||
Self::DoneDate => 10,
|
||||
Self::ActivityDate => 20,
|
||||
Self::AddedDate => 20,
|
||||
Self::Availability => 10,
|
||||
Self::BandwidthPriority => 10,
|
||||
Self::Comment => 20,
|
||||
Self::CorruptEver => 15,
|
||||
Self::Creator => 20,
|
||||
Self::DateCreated => 20,
|
||||
Self::DesiredAvailable => 15,
|
||||
Self::DoneDate => 20,
|
||||
Self::DownloadDir => 30,
|
||||
Self::DownloadLimit => todo!(),
|
||||
Self::DownloadLimited => todo!(),
|
||||
Self::DownloadedEver => todo!(),
|
||||
Self::EditDate => 10,
|
||||
Self::Error => 10,
|
||||
Self::ErrorString => 10,
|
||||
Self::DownloadLimit => 15,
|
||||
Self::DownloadLimited => 10,
|
||||
Self::DownloadedEver => 15,
|
||||
Self::EditDate => 20,
|
||||
Self::Error => 15,
|
||||
Self::ErrorString => 20,
|
||||
Self::Eta => 10,
|
||||
Self::EtaIdle => todo!(),
|
||||
Self::FileCount => todo!(),
|
||||
Self::EtaIdle => 10,
|
||||
Self::FileCount => 10,
|
||||
Self::FileStats => 10,
|
||||
Self::Files => 10,
|
||||
Self::Group => todo!(),
|
||||
Self::HashString => 10,
|
||||
Self::HaveUnchecked => todo!(),
|
||||
Self::HaveValid => todo!(),
|
||||
Self::HonorsSessionLimits => todo!(),
|
||||
Self::Id => 10,
|
||||
Self::Group => 10,
|
||||
Self::HashString => 42,
|
||||
Self::HaveUnchecked => 15,
|
||||
Self::HaveValid => 15,
|
||||
Self::HonorsSessionLimits => 10,
|
||||
Self::Id => 5,
|
||||
Self::IsFinished => 10,
|
||||
Self::IsPrivate => 10,
|
||||
Self::IsStalled => 10,
|
||||
Self::Labels => 10,
|
||||
Self::LeftUntilDone => 10,
|
||||
Self::MagnetLink => todo!(),
|
||||
Self::ManualAnnounceTime => todo!(),
|
||||
Self::MaxConnectedPeers => todo!(),
|
||||
Self::Labels => 20,
|
||||
Self::LeftUntilDone => 15,
|
||||
Self::MagnetLink => 50,
|
||||
Self::ManualAnnounceTime => 20,
|
||||
Self::MaxConnectedPeers => 10,
|
||||
Self::MetadataPercentComplete => 10,
|
||||
Self::Name => 70,
|
||||
Self::PeerLimit => todo!(),
|
||||
Self::Peers => todo!(),
|
||||
Self::PeerLimit => 10,
|
||||
Self::Peers => 10,
|
||||
Self::PeersConnected => 10,
|
||||
Self::PeersFrom => todo!(),
|
||||
Self::PeersFrom => 20,
|
||||
Self::PeersGettingFromUs => 10,
|
||||
Self::PeersSendingToUs => 10,
|
||||
Self::PercentComplete => todo!(),
|
||||
Self::PercentComplete => 10,
|
||||
Self::PercentDone => 10,
|
||||
Self::PieceCount => todo!(),
|
||||
Self::PieceSize => todo!(),
|
||||
Self::Pieces => todo!(),
|
||||
Self::PrimaryMimeType => todo!(),
|
||||
Self::PieceCount => 10,
|
||||
Self::PieceSize => 15,
|
||||
Self::Pieces => 20,
|
||||
Self::PrimaryMimeType => 20,
|
||||
Self::Priorities => 10,
|
||||
Self::QueuePosition => 10,
|
||||
Self::RateDownload => 10,
|
||||
Self::RateUpload => 10,
|
||||
Self::RateDownload => 15,
|
||||
Self::RateUpload => 15,
|
||||
Self::RecheckProgress => 10,
|
||||
Self::SecondsDownloading => todo!(),
|
||||
Self::SecondsSeeding => 10,
|
||||
Self::SeedIdleLimit => todo!(),
|
||||
Self::SeedIdleMode => todo!(),
|
||||
Self::SecondsDownloading => 15,
|
||||
Self::SecondsSeeding => 15,
|
||||
Self::SeedIdleLimit => 10,
|
||||
Self::SeedIdleMode => 15,
|
||||
Self::SeedRatioLimit => 10,
|
||||
Self::SeedRatioMode => 10,
|
||||
Self::SequentialDownload => todo!(),
|
||||
Self::SizeWhenDone => 10,
|
||||
Self::StartDate => todo!(),
|
||||
Self::SeedRatioMode => 15,
|
||||
Self::SequentialDownload => 10,
|
||||
Self::SizeWhenDone => 15,
|
||||
Self::StartDate => 20,
|
||||
Self::Status => 15,
|
||||
Self::TorrentFile => 10,
|
||||
Self::TotalSize => 10,
|
||||
Self::TrackerList => todo!(),
|
||||
Self::TrackerStats => todo!(),
|
||||
Self::TorrentFile => 30,
|
||||
Self::TotalSize => 15,
|
||||
Self::TrackerList => 30,
|
||||
Self::TrackerStats => 10,
|
||||
Self::Trackers => 10,
|
||||
Self::UploadLimit => todo!(),
|
||||
Self::UploadLimited => todo!(),
|
||||
Self::UploadLimit => 15,
|
||||
Self::UploadLimited => 10,
|
||||
Self::UploadRatio => 10,
|
||||
Self::UploadedEver => 10,
|
||||
Self::UploadedEver => 15,
|
||||
Self::Wanted => 10,
|
||||
Self::Webseeds => todo!(),
|
||||
Self::Webseeds => 20,
|
||||
Self::WebseedsSendingToUs => 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,24 +1,159 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
pub struct NetSpeed(i64);
|
||||
#[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 },
|
||||
}
|
||||
|
||||
impl From<i64> for NetSpeed {
|
||||
fn from(bytes_per_second: i64) -> Self {
|
||||
NetSpeed(bytes_per_second)
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct NetSpeed(u64);
|
||||
|
||||
impl NetSpeed {
|
||||
pub const fn new(bytes_per_second: u64) -> Self {
|
||||
Self(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 fmt::Display for NetSpeed {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let bytes_per_second = self.0;
|
||||
if bytes_per_second < 1024 {
|
||||
write!(f, "{} B/s", bytes_per_second)
|
||||
} else if bytes_per_second < 1024 * 1024 {
|
||||
write!(f, "{:.2} KB/s", bytes_per_second as f64 / 1024.0)
|
||||
} else if bytes_per_second < 1024 * 1024 * 1024 {
|
||||
write!(f, "{:.2} MB/s", bytes_per_second as f64 / (1024.0 * 1024.0))
|
||||
} else {
|
||||
write!(f, "{:.2} GB/s", bytes_per_second as f64 / (1024.0 * 1024.0 * 1024.0))
|
||||
macro_rules! impl_from_unsigned {
|
||||
($($t:ty),*) => {
|
||||
$(
|
||||
impl From<$t> for NetSpeed {
|
||||
fn from(value: $t) -> Self {
|
||||
Self(value as u64)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_try_from_signed {
|
||||
($($t:ty),*) => {
|
||||
$(
|
||||
impl TryFrom<$t> for NetSpeed {
|
||||
type Error = NetSpeedError;
|
||||
|
||||
fn try_from(value: $t) -> Result<Self, Self::Error> {
|
||||
if value < 0 {
|
||||
Err(NetSpeedError::NegativeSpeed { value: value as i64 })
|
||||
} else {
|
||||
Ok(Self(value as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
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 })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,6 @@ pub enum Event {
|
||||
/// Terminal event handler.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
/// TODO: write description
|
||||
pub struct EventHandler {
|
||||
/// Event sender channel.
|
||||
sender: mpsc::Sender<Event>,
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
mod popup;
|
||||
mod table;
|
||||
|
||||
use crate::app::{App, Tab};
|
||||
use popup::render_popup;
|
||||
use ratatui::{
|
||||
layout::Alignment,
|
||||
prelude::{Constraint, Direction, Layout},
|
||||
@ -6,12 +11,7 @@ use ratatui::{
|
||||
widgets::{Block, BorderType, Borders, Clear, Tabs},
|
||||
Frame,
|
||||
};
|
||||
mod popup;
|
||||
mod table;
|
||||
|
||||
use crate::app::{App, Tab};
|
||||
|
||||
use self::{popup::render_popup, table::render_table};
|
||||
use table::render_table;
|
||||
|
||||
/// Renders the user interface widgets.
|
||||
pub fn render(app: &mut App, frame: &mut Frame) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user