#[derive(Debug, Clone)]
pub struct ProgressInfo {
pub percentage: f32,
pub bytes_processed: u64,
pub total_bytes: u64,
pub stage: ProcessingStage,
pub message: Option<String>,
}
impl ProgressInfo {
pub fn new(stage: ProcessingStage, bytes_processed: u64, total_bytes: u64) -> Self {
let percentage = if total_bytes > 0 {
(bytes_processed as f32 / total_bytes as f32) * 100.0
} else {
100.0
};
Self {
percentage: percentage.clamp(0.0, 100.0),
bytes_processed,
total_bytes,
stage,
message: None,
}
}
pub fn with_message<S: Into<String>>(mut self, message: S) -> Self {
self.message = Some(message.into());
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ProcessingStage {
Reading,
ComputingFrequencies,
BuildingTree,
Encoding,
Encrypting,
Decrypting,
Decoding,
Writing,
Finalizing,
}
impl fmt::Display for ProcessingStage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProcessingStage::Reading => write!(f, "Reading"),
ProcessingStage::ComputingFrequencies => write!(f, "Computing frequencies"),
ProcessingStage::BuildingTree => write!(f, "Building Huffman tree"),
ProcessingStage::Encoding => write!(f, "Encoding"),
ProcessingStage::Encrypting => write!(f, "Encrypting"),
ProcessingStage::Decrypting => write!(f, "Decrypting"),
ProcessingStage::Decoding => write!(f, "Decoding"),
ProcessingStage::Writing => write!(f, "Writing"),
ProcessingStage::Finalizing => write!(f, "Finalizing"),
}
}
}
use std::fmt;
pub type ProgressCallback = Box<dyn FnMut(&ProgressInfo) + Send + Sync>;
pub struct ProgressBuilder {
callback: Option<ProgressCallback>,
}
impl std::fmt::Debug for ProgressBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProgressBuilder")
.field("callback", &"<callback>")
.finish()
}
}
impl ProgressBuilder {
pub fn new() -> Self {
Self { callback: None }
}
pub fn with_callback<F>(mut self, callback: F) -> Self
where
F: FnMut(&ProgressInfo) + Send + Sync + 'static,
{
self.callback = Some(Box::new(callback));
self
}
pub fn build(self) -> Option<ProgressCallback> {
self.callback
}
}
impl Default for ProgressBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct ProgressTracker {
total_bytes: u64,
bytes_processed: u64,
stage: ProcessingStage,
callback: Option<ProgressCallback>,
}
impl ProgressTracker {
pub fn new(
total_bytes: u64,
stage: ProcessingStage,
callback: Option<ProgressCallback>,
) -> Self {
Self {
total_bytes,
bytes_processed: 0,
stage,
callback,
}
}
pub fn update(&mut self, bytes: u64) {
self.bytes_processed += bytes;
self.notify();
}
pub fn set_stage(&mut self, stage: ProcessingStage) {
self.stage = stage;
self.notify();
}
pub fn add_total_bytes(&mut self, additional: u64) {
self.total_bytes += additional;
self.notify();
}
pub fn notify(&mut self) {
if let Some(ref mut callback) = self.callback {
let info =
ProgressInfo::new(self.stage.clone(), self.bytes_processed, self.total_bytes);
callback(&info);
}
}
pub fn percentage(&self) -> f32 {
if self.total_bytes > 0 {
(self.bytes_processed as f32 / self.total_bytes as f32) * 100.0
} else {
100.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_progress_info_creation() {
let info = ProgressInfo::new(ProcessingStage::Encoding, 500, 1000);
assert_eq!(info.percentage, 50.0);
assert_eq!(info.bytes_processed, 500);
assert_eq!(info.total_bytes, 1000);
assert_eq!(info.stage, ProcessingStage::Encoding);
}
#[test]
fn test_progress_info_clamping() {
let info = ProgressInfo::new(ProcessingStage::Encoding, 1500, 1000);
assert_eq!(info.percentage, 100.0);
let info = ProgressInfo::new(ProcessingStage::Encoding, 0, 1000);
assert_eq!(info.percentage, 0.0);
}
#[test]
fn test_progress_tracker() {
let mut tracker = ProgressTracker::new(1000, ProcessingStage::Encoding, None);
assert_eq!(tracker.percentage(), 0.0);
tracker.update(500);
assert_eq!(tracker.percentage(), 50.0);
tracker.set_stage(ProcessingStage::Finalizing);
assert_eq!(tracker.percentage(), 50.0);
}
#[test]
fn test_progress_callback() {
use std::sync::atomic::{AtomicU32, Ordering};
static CALL_COUNT: AtomicU32 = AtomicU32::new(0);
let callback = Box::new(|_: &ProgressInfo| {
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
});
let mut tracker = ProgressTracker::new(100, ProcessingStage::Encoding, Some(callback));
tracker.update(50);
tracker.update(50);
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 2); }
}