Skip to main content

gravityfile_ops/
progress.rs

1//! Progress reporting types for file operations.
2
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7use crate::OperationError;
8
9/// The type of operation being performed.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum OperationType {
12    Copy,
13    Move,
14    Delete,
15    Rename,
16    CreateFile,
17    CreateDirectory,
18}
19
20impl std::fmt::Display for OperationType {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            Self::Copy => write!(f, "Copy"),
24            Self::Move => write!(f, "Move"),
25            Self::Delete => write!(f, "Delete"),
26            Self::Rename => write!(f, "Rename"),
27            Self::CreateFile => write!(f, "Create file"),
28            Self::CreateDirectory => write!(f, "Create directory"),
29        }
30    }
31}
32
33/// Progress information for an ongoing operation.
34#[derive(Debug, Clone)]
35pub struct OperationProgress {
36    /// The type of operation.
37    pub operation_type: OperationType,
38    /// Number of files/directories completed.
39    pub files_completed: usize,
40    /// Total number of files/directories to process.
41    pub files_total: usize,
42    /// Number of bytes processed so far.
43    pub bytes_processed: u64,
44    /// Total bytes to process (may be 0 if unknown).
45    pub bytes_total: u64,
46    /// The file currently being processed.
47    pub current_file: Option<PathBuf>,
48    /// Errors encountered so far.
49    pub errors: Vec<OperationError>,
50}
51
52impl OperationProgress {
53    /// Create a new progress tracker for an operation.
54    pub fn new(operation_type: OperationType, files_total: usize, bytes_total: u64) -> Self {
55        Self {
56            operation_type,
57            files_completed: 0,
58            files_total,
59            bytes_processed: 0,
60            bytes_total,
61            current_file: None,
62            errors: Vec::new(),
63        }
64    }
65
66    /// Get the progress as a percentage (0.0 to 100.0).
67    pub fn percentage(&self) -> f64 {
68        if self.bytes_total > 0 {
69            (self.bytes_processed as f64 / self.bytes_total as f64) * 100.0
70        } else if self.files_total > 0 {
71            (self.files_completed as f64 / self.files_total as f64) * 100.0
72        } else {
73            0.0
74        }
75    }
76
77    /// Check if the operation has any errors.
78    pub fn has_errors(&self) -> bool {
79        !self.errors.is_empty()
80    }
81
82    /// Get the number of errors.
83    pub fn error_count(&self) -> usize {
84        self.errors.len()
85    }
86
87    /// Add an error to the progress.
88    pub fn add_error(&mut self, error: OperationError) {
89        self.errors.push(error);
90    }
91
92    /// Update the current file being processed.
93    pub fn set_current_file(&mut self, path: Option<PathBuf>) {
94        self.current_file = path;
95    }
96
97    /// Increment the completed count and add bytes.
98    pub fn complete_file(&mut self, bytes: u64) {
99        self.files_completed += 1;
100        self.bytes_processed += bytes;
101    }
102}
103
104/// Result of a completed operation.
105#[derive(Debug, Clone)]
106pub struct OperationComplete {
107    /// The type of operation.
108    pub operation_type: OperationType,
109    /// Number of items successfully processed.
110    pub succeeded: usize,
111    /// Number of items that failed.
112    pub failed: usize,
113    /// Total bytes processed.
114    pub bytes_processed: u64,
115    /// Errors that occurred.
116    pub errors: Vec<OperationError>,
117}
118
119impl OperationComplete {
120    /// Check if the operation was fully successful.
121    pub fn is_success(&self) -> bool {
122        self.failed == 0
123    }
124
125    /// Get a human-readable summary of the operation.
126    pub fn summary(&self) -> String {
127        let action = match self.operation_type {
128            OperationType::Copy => "Copied",
129            OperationType::Move => "Moved",
130            OperationType::Delete => "Deleted",
131            OperationType::Rename => "Renamed",
132            OperationType::CreateFile => "Created",
133            OperationType::CreateDirectory => "Created",
134        };
135
136        if self.failed == 0 {
137            format!("{} {} items", action, self.succeeded)
138        } else {
139            format!(
140                "{} {} items, {} failed",
141                action, self.succeeded, self.failed
142            )
143        }
144    }
145}