use crate::Result;
use std::path::{Path, PathBuf};
pub struct BackupManager {
backup_dir: PathBuf,
}
impl BackupManager {
pub fn new(backup_dir: PathBuf) -> Result<Self> {
std::fs::create_dir_all(&backup_dir).map_err(|e| {
crate::Error::BackupError(format!("Failed to create backup directory: {}", e))
})?;
Ok(Self { backup_dir })
}
pub fn create_backup(&self, file_path: &Path, job_id: &str) -> Result<PathBuf> {
if !file_path.exists() {
return Err(crate::Error::BackupError("File does not exist".to_string()));
}
let base_name = file_path
.file_name()
.ok_or_else(|| crate::Error::BackupError("Invalid filename".to_string()))?;
let job_dir = self.backup_dir.join(job_id);
let parent = job_dir.clone();
std::fs::create_dir_all(&parent).map_err(|e| {
crate::Error::BackupError(format!("Failed to create backup directory: {}", e))
})?;
let backup_path = {
let candidate = job_dir.join(base_name);
if !candidate.exists() {
candidate
} else {
let mut counter = 1;
loop {
let suffixed = job_dir.join(format!("{}.{}", base_name.to_string_lossy(), counter));
if !suffixed.exists() {
break suffixed;
}
counter += 1;
}
}
};
std::fs::copy(file_path, &backup_path)
.map_err(|e| crate::Error::BackupError(e.to_string()))?;
Ok(backup_path)
}
pub fn restore(&self, backup_path: &Path, target_path: &Path) -> Result<()> {
if !backup_path.exists() {
return Err(crate::Error::BackupError(
"Backup does not exist".to_string(),
));
}
std::fs::copy(backup_path, target_path)
.map_err(|e| crate::Error::BackupError(e.to_string()))?;
Ok(())
}
pub fn cleanup(&self, older_than_secs: u64) -> Result<()> {
use std::time::{SystemTime, UNIX_EPOCH};
let cutoff = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
.saturating_sub(older_than_secs);
if !self.backup_dir.exists() {
return Ok(());
}
for entry in std::fs::read_dir(&self.backup_dir)
.map_err(|e| crate::Error::BackupError(e.to_string()))?
{
let entry = entry.map_err(|e| crate::Error::BackupError(e.to_string()))?;
let path = entry.path();
if !path.is_dir() {
continue;
}
let mtime = path
.metadata()
.map_err(|e| crate::Error::BackupError(e.to_string()))?
.modified()
.map_err(|e| crate::Error::BackupError(e.to_string()))?
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
if mtime < cutoff {
std::fs::remove_dir_all(&path)
.map_err(|e| crate::Error::BackupError(e.to_string()))?;
}
}
Ok(())
}
}