stakpak_shared/
file_backup_manager.rs1use crate::local_store::LocalStore;
2use crate::remote_connection::RemoteConnection;
3use crate::remote_store::RemoteStore;
4use std::path::Path;
5use std::sync::Arc;
6use uuid::Uuid;
7
8pub struct FileBackupManager;
10
11impl FileBackupManager {
12 pub fn move_local_path_to_backup(path: &str) -> Result<String, String> {
14 let path_obj = Path::new(path);
15
16 if !path_obj.exists() {
17 return Err(format!("Path does not exist: {}", path));
18 }
19
20 let backup_session_id = Uuid::new_v4().to_string();
21
22 let backup_session_path = LocalStore::get_backup_session_path(&backup_session_id);
23 let full_backup_dir = LocalStore::get_local_session_store_path().join(&backup_session_path);
24
25 if let Err(e) = std::fs::create_dir_all(&full_backup_dir) {
26 return Err(format!("Failed to create backup directory: {}", e));
27 }
28
29 let item_name = path_obj
30 .file_name()
31 .and_then(|n| n.to_str())
32 .unwrap_or("unknown_item");
33 let backup_path = full_backup_dir.join(item_name);
34
35 match std::fs::rename(path_obj, &backup_path) {
36 Ok(()) => Ok(backup_path.to_string_lossy().to_string()),
37 Err(e) => Err(format!(
38 "Failed to move local path '{}' to backup: {}",
39 path, e
40 )),
41 }
42 }
43
44 pub async fn move_remote_path_to_backup(
46 conn: &Arc<RemoteConnection>,
47 path: &str,
48 ) -> Result<String, String> {
49 let backup_session_id = Uuid::new_v4().to_string();
50
51 let absolute_backup_dir =
52 match RemoteStore::get_absolute_backup_session_path(conn, &backup_session_id).await {
53 Ok(abs_path) => abs_path,
54 Err(e) => return Err(e),
55 };
56
57 let item_name = Path::new(&path)
58 .file_name()
59 .and_then(|n| n.to_str())
60 .unwrap_or("unknown_item");
61 let backup_path = format!("{}/{}", absolute_backup_dir, item_name);
62
63 match conn.rename(path, &backup_path).await {
64 Ok(()) => Ok(backup_path),
65 Err(e) => Err(format!(
66 "Failed to move remote path '{}' to backup: {}",
67 path, e
68 )),
69 }
70 }
71
72 pub fn format_backup_xml(
74 backup_mapping: &std::collections::HashMap<String, String>,
75 location: &str,
76 ) -> String {
77 let mut inner_content = String::new();
78
79 for (original_path, backup_path) in backup_mapping {
80 inner_content.push_str(&format!(
81 "\n <file\n original_path=\"{}\"\n backup_path=\"{}\"\n location=\"{}\"\n />",
82 Self::escape_xml(original_path),
83 Self::escape_xml(backup_path),
84 location
85 ));
86 }
87
88 format!("<file_backups>{}\n</file_backups>", inner_content)
89 }
90
91 fn escape_xml(text: &str) -> String {
93 text.replace('&', "&")
94 .replace('<', "<")
95 .replace('>', ">")
96 .replace('"', """)
97 .replace('\'', "'")
98 }
99}