use tokio::sync::broadcast;
use tokio_stream::wrappers::BroadcastStream;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ProgressInfo {
pub downloaded: u64,
pub total: u64,
}
impl ProgressInfo {
pub fn new(downloaded: u64, total: u64) -> Self {
Self { downloaded, total }
}
pub fn percentage(&self) -> f64 {
if self.total == 0 {
0.0
} else {
self.downloaded as f64 / self.total as f64
}
}
}
impl std::fmt::Display for ProgressInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ProgressInfo(downloaded={}, total={}, percent={:.1}%)",
self.downloaded,
self.total,
self.percentage() * 100.0
)
}
}
#[derive(Debug)]
pub struct ProgressTracker {
tx: broadcast::Sender<ProgressInfo>,
}
impl ProgressTracker {
pub fn new() -> Self {
let (tx, _) = broadcast::channel(100);
tracing::debug!(capacity = 100, "⚙️ Created new progress tracker");
Self { tx }
}
pub fn update(&self, downloaded: u64, total: u64) {
let percentage = if total > 0 {
(downloaded as f64 / total as f64) * 100.0
} else {
0.0
};
tracing::debug!(
downloaded = downloaded,
total = total,
percentage = percentage,
"📥 Progress updated"
);
let _ = self.tx.send(ProgressInfo::new(downloaded, total));
}
pub fn stream(&self) -> BroadcastStream<ProgressInfo> {
tracing::debug!("📥 Creating progress stream");
BroadcastStream::new(self.tx.subscribe())
}
pub fn callback(&self) -> impl Fn(u64, u64) + Send + Sync + 'static {
tracing::debug!("⚙️ Creating progress callback");
let tx = self.tx.clone();
move |downloaded, total| {
let _ = tx.send(ProgressInfo::new(downloaded, total));
}
}
}
impl Default for ProgressTracker {
fn default() -> Self {
Self::new()
}
}