use crate::migration::functions::MigrationProgressCallback;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MigrationPhase {
Capturing,
Transferring,
Validating,
Restoring,
Completed,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MigrationProgressEvent {
Started {
agent_id: [u8; 16],
total_bytes: usize,
},
CapturingState {
agent_id: [u8; 16],
percent: u8,
},
TransferProgress {
agent_id: [u8; 16],
transferred_bytes: usize,
total_bytes: usize,
percent: u8,
},
Validating {
agent_id: [u8; 16],
step: String,
},
Restoring {
agent_id: [u8; 16],
percent: u8,
},
Completed {
agent_id: [u8; 16],
duration_ms: u64,
bytes_transferred: usize,
},
Failed {
agent_id: [u8; 16],
error: String,
},
}
impl MigrationProgressEvent {
pub fn agent_id(&self) -> [u8; 16] {
match self {
Self::Started { agent_id, .. }
| Self::CapturingState { agent_id, .. }
| Self::TransferProgress { agent_id, .. }
| Self::Validating { agent_id, .. }
| Self::Restoring { agent_id, .. }
| Self::Completed { agent_id, .. }
| Self::Failed { agent_id, .. } => *agent_id,
}
}
pub fn progress_percent(&self) -> Option<u8> {
match self {
Self::CapturingState { percent, .. }
| Self::TransferProgress { percent, .. }
| Self::Restoring { percent, .. } => Some(*percent),
_ => None,
}
}
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Completed { .. } | Self::Failed { .. })
}
}
pub struct MigrationProgressTracker {
agent_id: [u8; 16],
total_bytes: usize,
transferred_bytes: usize,
start_time: std::time::Instant,
phase: MigrationPhase,
callback: Option<Box<dyn MigrationProgressCallback>>,
}
impl MigrationProgressTracker {
pub fn new(agent_id: [u8; 16], total_bytes: usize) -> Self {
Self {
agent_id,
total_bytes,
transferred_bytes: 0,
start_time: std::time::Instant::now(),
phase: MigrationPhase::Capturing,
callback: None,
}
}
pub fn with_callback<C: MigrationProgressCallback + 'static>(mut self, callback: C) -> Self {
self.callback = Some(Box::new(callback));
self
}
pub fn report_started(&mut self) {
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::Started {
agent_id: self.agent_id,
total_bytes: self.total_bytes,
});
}
}
pub fn update_capturing_progress(&mut self, percent: u8) {
self.phase = MigrationPhase::Capturing;
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::CapturingState {
agent_id: self.agent_id,
percent,
});
}
}
pub fn update_transfer_progress(&mut self, transferred_bytes: usize) {
self.phase = MigrationPhase::Transferring;
self.transferred_bytes = transferred_bytes;
let percent = if self.total_bytes > 0 {
((transferred_bytes as f64 / self.total_bytes as f64) * 100.0) as u8
} else {
100
};
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::TransferProgress {
agent_id: self.agent_id,
transferred_bytes,
total_bytes: self.total_bytes,
percent,
});
}
}
pub fn report_validating(&mut self, step: String) {
self.phase = MigrationPhase::Validating;
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::Validating {
agent_id: self.agent_id,
step,
});
}
}
pub fn update_restoring_progress(&mut self, percent: u8) {
self.phase = MigrationPhase::Restoring;
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::Restoring {
agent_id: self.agent_id,
percent,
});
}
}
pub fn report_completed(&mut self) {
self.phase = MigrationPhase::Completed;
let duration_ms = self.start_time.elapsed().as_millis() as u64;
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::Completed {
agent_id: self.agent_id,
duration_ms,
bytes_transferred: self.transferred_bytes,
});
}
}
pub fn report_failed(&mut self, error: String) {
self.phase = MigrationPhase::Failed;
if let Some(ref mut callback) = self.callback {
callback.on_progress(MigrationProgressEvent::Failed {
agent_id: self.agent_id,
error,
});
}
}
pub fn phase(&self) -> MigrationPhase {
self.phase
}
pub fn elapsed_ms(&self) -> u64 {
self.start_time.elapsed().as_millis() as u64
}
pub fn progress_percent(&self) -> u8 {
if self.total_bytes == 0 {
return 100;
}
((self.transferred_bytes as f64 / self.total_bytes as f64) * 100.0) as u8
}
}