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