use std::{collections::HashMap, time::SystemTime}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use crate::misc::RollingAverage; #[derive(Clone, Debug)] pub enum DlStatus { Init { bytes_total: u64, filename: String }, Update { speed_mbps: f32, bytes_curr: u64 }, Done { duration_ms: u64 }, DoneErr { filename: String }, Skipped, Message(String), } #[derive(Clone, Debug)] pub struct DlReport { pub id: u32, pub status: DlStatus, } #[derive(Clone)] pub struct DlReporter { id: u32, transmitter: mpsc::UnboundedSender, } impl DlReporter { pub fn new(id: u32, transmitter: mpsc::UnboundedSender) -> DlReporter { DlReporter { id, transmitter } } pub fn send(&self, status: DlStatus) { // This should not fail, so unwrap it here instead propagating the error self.transmitter .send(DlReport { id: self.id, status, }) .unwrap(); } pub fn init(&self, bytes_total: u64, filename: String) { self.send(DlStatus::Init { bytes_total, filename, }) } pub fn update(&self, speed_mbps: f32, bytes_curr: u64) { self.send(DlStatus::Update { speed_mbps, bytes_curr, }) } pub fn done(&self, duration_ms: u64) { self.send(DlStatus::Done { duration_ms }) } pub fn done_err(&self, filename: String) { self.send(DlStatus::DoneErr { filename }) } pub fn skipped(&self) { self.send(DlStatus::Skipped); } pub fn msg(&self, msg: String) { self.send(DlStatus::Message(msg)); } } #[macro_export] macro_rules! report_msg { ($rep:ident, $fmt:expr) => { DlReporter::msg(&$rep, format!($fmt)); }; ($rep:ident, $fmt:expr, $($fmt2:expr),+) => { DlReporter::msg(&$rep, format!($fmt, $($fmt2,)+)); }; } pub struct InfoHolder { pub filename: String, pub total_size: u64, pub progress: u64, pub speed_mbps: f32, } impl InfoHolder { pub fn new(filename: String, total_size: u64) -> InfoHolder { InfoHolder { filename, total_size, progress: 0, speed_mbps: 0.0, } } } pub struct DlReportAccumulator { parent: DlReporter, rec: UnboundedReceiver, } impl DlReportAccumulator { pub fn new(parent: DlReporter) -> (DlReportAccumulator, UnboundedSender) { let (tx, rec) = mpsc::unbounded_channel(); (DlReportAccumulator { parent, rec }, tx) } pub async fn accumulate(mut self) { let mut progresses: HashMap = HashMap::new(); let mut progress_last: u64 = 0; let mut t_last = SystemTime::now(); let mut average_speed = RollingAverage::new(10); while let Some(update) = self.rec.recv().await { match update.status { DlStatus::Init { bytes_total: _, filename: _, } => {} DlStatus::Update { speed_mbps: _, bytes_curr, } => { *progresses.entry(update.id).or_insert(0) = bytes_curr; let progress_curr = progresses.values().sum(); let progress_delta = progress_curr - progress_last; let t_elapsed = t_last.elapsed().unwrap().as_secs_f64(); let speed_mbps = average_speed.value() as f32; // currently executes always, but might change if progress_delta >= 5_000_000 { average_speed.add(((progress_delta as f64) / 1_000_000.0) / t_elapsed); progress_last = progress_curr; t_last = SystemTime::now(); } self.parent.update(speed_mbps, progress_curr); } DlStatus::Done { duration_ms: _ } => {} // Just forwared everything else to the calling receiver _ => self.parent.send(update.status), } } } }