rqbit/crates/librqbit/src/torrent_state/stats.rs
2023-11-25 11:21:45 +00:00

198 lines
5.2 KiB
Rust

use std::time::Duration;
use serde::Serialize;
use super::{live::stats::snapshot::StatsSnapshot, TorrentStateLive};
use size_format::SizeFormatterBinary as SF;
#[derive(Serialize, Default, Debug)]
pub struct LiveStats {
pub snapshot: StatsSnapshot,
pub average_piece_download_time: Option<Duration>,
pub download_speed: Speed,
pub time_remaining: Option<DurationWithHumanReadable>,
}
impl std::fmt::Display for LiveStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "down speed: {}", self.download_speed)?;
if let Some(time_remaining) = &self.time_remaining {
write!(f, " eta: {time_remaining}")?;
}
Ok(())
}
}
impl From<&TorrentStateLive> for LiveStats {
fn from(live: &TorrentStateLive) -> Self {
let snapshot = live.stats_snapshot();
let estimator = live.speed_estimator();
Self {
average_piece_download_time: snapshot.average_piece_download_time(),
snapshot,
download_speed: estimator.download_mbps().into(),
time_remaining: estimator.time_remaining().map(DurationWithHumanReadable),
}
}
}
#[derive(Serialize, Debug)]
pub struct TorrentStats {
pub state: &'static str,
pub error: Option<String>,
pub progress_bytes: u64,
pub total_bytes: u64,
pub finished: bool,
pub live: Option<LiveStats>,
}
impl std::fmt::Display for TorrentStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: ", self.state)?;
if let Some(error) = &self.error {
return write!(f, "{error}");
}
write!(
f,
"{} ({})",
self.progress_percent_human_readable(),
self.progress_bytes_human_readable()
)?;
if let Some(live) = &self.live {
write!(f, " [{live}]")?;
}
Ok(())
}
}
impl TorrentStats {
pub fn progress_percent_human_readable(&self) -> impl std::fmt::Display {
struct Percents {
progress: u64,
total: u64,
}
impl std::fmt::Display for Percents {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.total == 0 {
return write!(f, "N/A");
}
let pct = self.progress as f64 / self.total as f64 * 100f64;
write!(f, "{pct:.2}%")
}
}
Percents {
progress: self.progress_bytes,
total: self.total_bytes,
}
}
pub fn progress_bytes_human_readable(&self) -> impl std::fmt::Display {
struct Progress {
progress: u64,
total: u64,
}
impl std::fmt::Display for Progress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} / {}", SF::new(self.progress), SF::new(self.total))
}
}
Progress {
progress: self.progress_bytes,
total: self.total_bytes,
}
}
}
fn format_seconds_to_time(seconds: u64, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let hours = seconds / 3600;
let minutes = (seconds % 3600) / 60;
let seconds = seconds % 60;
if hours > 0 {
write!(f, "{}h {}m", hours, minutes)
} else if minutes > 0 {
write!(f, "{}m {}s", minutes, seconds)
} else {
write!(f, "{}s", seconds)
}
}
pub struct DurationWithHumanReadable(Duration);
impl core::fmt::Display for DurationWithHumanReadable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> core::fmt::Result {
format_seconds_to_time(self.0.as_secs(), f)
}
}
impl core::fmt::Debug for DurationWithHumanReadable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl Serialize for DurationWithHumanReadable {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
struct Tmp {
duration: Duration,
human_readable: String,
}
Tmp {
duration: self.0,
human_readable: format!("{}", self),
}
.serialize(serializer)
}
}
#[derive(Default)]
pub struct Speed {
pub mbps: f64,
}
impl Speed {
fn new(mbps: f64) -> Self {
Self { mbps }
}
}
impl From<f64> for Speed {
fn from(mbps: f64) -> Self {
Self::new(mbps)
}
}
impl core::fmt::Display for Speed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:.2} MiB/s", self.mbps)
}
}
impl core::fmt::Debug for Speed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl Serialize for Speed {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
struct Tmp {
mbps: f64,
human_readable: String,
}
Tmp {
mbps: self.mbps,
human_readable: format!("{}", self),
}
.serialize(serializer)
}
}