use crate::error::RoboSyncError;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct ErrorDetail {
pub path: PathBuf,
pub operation: String,
pub message: String,
}
#[derive(Debug)]
pub struct StructuredError {
pub error: RoboSyncError,
pub context: String,
}
#[derive(Debug, Default)]
pub struct SyncStats {
files_processed: AtomicU64,
files_copied: AtomicU64,
files_deleted: AtomicU64,
bytes_transferred: AtomicU64,
blocks_matched: AtomicU64,
errors: AtomicU64,
pub elapsed_time: Duration,
pub warnings: Arc<Mutex<Vec<String>>>,
pub error_report_path: Option<PathBuf>,
pub error_details: Arc<Mutex<Vec<ErrorDetail>>>,
pub structured_errors: Arc<Mutex<Vec<StructuredError>>>,
}
impl SyncStats {
pub fn new() -> Self {
Self::default()
}
pub fn add_bytes_transferred(&self, bytes: u64) {
self.bytes_transferred.fetch_add(bytes, Ordering::Relaxed);
}
pub fn set_bytes_transferred(&self, bytes: u64) {
self.bytes_transferred.store(bytes, Ordering::Relaxed);
}
pub fn bytes_transferred(&self) -> u64 {
self.bytes_transferred.load(Ordering::Relaxed)
}
pub fn increment_files_processed(&self) {
self.files_processed.fetch_add(1, Ordering::Relaxed);
}
pub fn increment_files_copied(&self) {
self.files_copied.fetch_add(1, Ordering::Relaxed);
}
pub fn increment_files_deleted(&self) {
self.files_deleted.fetch_add(1, Ordering::Relaxed);
}
pub fn increment_errors(&self) {
self.errors.fetch_add(1, Ordering::Relaxed);
}
pub fn add_error(&self, path: PathBuf, operation: &str, message: &str) {
self.increment_errors();
if let Ok(mut errors) = self.error_details.lock() {
errors.push(ErrorDetail {
path,
operation: operation.to_string(),
message: message.to_string(),
});
}
}
pub fn add_structured_error(&self, error: RoboSyncError, context: impl Into<String>) {
self.increment_errors();
if let Ok(mut errors) = self.structured_errors.lock() {
errors.push(StructuredError {
error,
context: context.into(),
});
}
}
pub fn get_error_details(&self) -> Vec<ErrorDetail> {
self.error_details
.lock()
.unwrap_or_else(|e| e.into_inner())
.clone()
}
pub fn get_structured_errors(&self) -> Vec<StructuredError> {
self.structured_errors
.lock()
.unwrap_or_else(|e| e.into_inner())
.drain(..)
.collect()
}
pub fn add_blocks_matched(&self, blocks: u64) {
self.blocks_matched.fetch_add(blocks, Ordering::Relaxed);
}
pub fn add_warning(&self, warning: String) {
if let Ok(mut warnings) = self.warnings.lock() {
warnings.push(warning);
}
}
pub fn get_warnings(&self) -> Vec<String> {
self.warnings
.lock()
.unwrap_or_else(|e| e.into_inner())
.clone()
}
pub fn files_processed(&self) -> u64 {
self.files_processed.load(Ordering::Relaxed)
}
pub fn files_copied(&self) -> u64 {
self.files_copied.load(Ordering::Relaxed)
}
pub fn files_deleted(&self) -> u64 {
self.files_deleted.load(Ordering::Relaxed)
}
pub fn errors(&self) -> u64 {
self.errors.load(Ordering::Relaxed)
}
pub fn blocks_matched(&self) -> u64 {
self.blocks_matched.load(Ordering::Relaxed)
}
pub fn set_error_report_path(&mut self, path: PathBuf) {
self.error_report_path = Some(path);
}
}
impl Clone for SyncStats {
fn clone(&self) -> Self {
Self {
files_processed: AtomicU64::new(self.files_processed()),
files_copied: AtomicU64::new(self.files_copied()),
files_deleted: AtomicU64::new(self.files_deleted()),
bytes_transferred: AtomicU64::new(self.bytes_transferred()),
blocks_matched: AtomicU64::new(self.blocks_matched()),
errors: AtomicU64::new(self.errors()),
elapsed_time: self.elapsed_time,
warnings: Arc::new(Mutex::new(self.get_warnings())),
error_report_path: self.error_report_path.clone(),
error_details: Arc::new(Mutex::new(self.get_error_details())),
structured_errors: Arc::new(Mutex::new(Vec::new())), }
}
}