diff options
Diffstat (limited to 'src/backup.rs')
-rw-r--r-- | src/backup.rs | 126 |
1 files changed, 76 insertions, 50 deletions
diff --git a/src/backup.rs b/src/backup.rs index 8cc94f1..a643cb2 100644 --- a/src/backup.rs +++ b/src/backup.rs | |||
@@ -5,7 +5,6 @@ use std::{ | |||
5 | time::{SystemTime, UNIX_EPOCH}, | 5 | time::{SystemTime, UNIX_EPOCH}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use gethostname::gethostname; | ||
9 | use serde::{Deserialize, Serialize}; | 8 | use serde::{Deserialize, Serialize}; |
10 | use uuid::Uuid; | 9 | use uuid::Uuid; |
11 | 10 | ||
@@ -20,10 +19,11 @@ pub type BackupId = String; | |||
20 | 19 | ||
21 | #[derive(Debug, Serialize, Deserialize)] | 20 | #[derive(Debug, Serialize, Deserialize)] |
22 | pub struct Backup { | 21 | pub struct Backup { |
23 | id: String, | 22 | pub id: String, |
24 | timestamp: u64, | 23 | timestamp: u64, |
25 | packages: Vec<Package>, | 24 | packages: Vec<Package>, |
26 | files: Vec<PathInfo>, | 25 | pub files: Vec<PathInfo>, |
26 | device: String, | ||
27 | } | 27 | } |
28 | 28 | ||
29 | impl Backup { | 29 | impl Backup { |
@@ -33,63 +33,82 @@ impl Backup { | |||
33 | files.push(PathInfo::from_path(config, dir)?); | 33 | files.push(PathInfo::from_path(config, dir)?); |
34 | } | 34 | } |
35 | Ok(Self { | 35 | Ok(Self { |
36 | // UUID not really needed, maybe a shorter hash | 36 | // TODO: UUID not really needed, maybe a shorter hash |
37 | id: Uuid::new_v4().to_string(), | 37 | id: Uuid::new_v4().to_string(), |
38 | timestamp: Self::get_timestamp(), | 38 | timestamp: Self::get_timestamp(), |
39 | packages, | 39 | packages, |
40 | files, | 40 | files, |
41 | device: config.device.clone(), | ||
41 | }) | 42 | }) |
42 | } | 43 | } |
43 | 44 | ||
44 | pub fn save(&self, config: &Config) -> Result<()> { | 45 | pub fn save(&self, config: &Config) -> Result<()> { |
45 | let rel_location = format!( | 46 | println!("Save Backup {:?}", self.get_location(config)); |
46 | "{}_{}", | 47 | // println!("{self:#?}"); |
47 | gethostname() | 48 | self.get_location(config).append_to_root(config)?; |
48 | .into_string() | ||
49 | .map_err(|_| Error::InvalidOsString)?, | ||
50 | Self::get_timestamp() | ||
51 | ); | ||
52 | |||
53 | let bl = BackupLocation { | ||
54 | id: self.id.to_string(), | ||
55 | rel_location, | ||
56 | }; | ||
57 | |||
58 | Self::append_to_root_index(config, bl.clone())?; | ||
59 | 49 | ||
60 | let backup_root = format!("{}/{}", config.root, bl.rel_location); | 50 | let backup_root = self.get_location(config).get_absolute_dir(config); |
61 | create_dir_all(&backup_root).unwrap(); | 51 | create_dir_all(&backup_root).unwrap(); |
62 | let path = format!("{}/index.json", backup_root); | 52 | let path = format!("{}/index.json", backup_root); |
63 | let mut f = File::create(path).unwrap(); | 53 | let mut f = File::create(path).unwrap(); |
64 | f.write_all(&serde_json::to_vec(self).unwrap()).unwrap(); | 54 | f.write_all(&serde_json::to_vec(self).unwrap()).unwrap(); |
65 | 55 | ||
56 | for path in &self.files { | ||
57 | path.save(&backup_root)?; | ||
58 | } | ||
59 | |||
66 | Ok(()) | 60 | Ok(()) |
67 | } | 61 | } |
68 | 62 | ||
69 | pub fn get_index(config: &Config, id: Option<BackupId>) -> Result<Self> { | 63 | pub fn get_last(config: &Config) -> Result<Option<Self>> { |
70 | let backup_index_root = format!("{}/index.json", config.root); | 64 | let backup_index_root = format!("{}/index.json", config.root); |
71 | let list: Vec<BackupLocation> = Self::get_json_content(&backup_index_root)?; | 65 | let list: Vec<BackupLocation> = match Self::get_json_content(&backup_index_root) { |
72 | println!("{list:#?}"); | 66 | Ok(list) => list, |
73 | 67 | Err(err) => { | |
74 | let index_loc = if let Some(id) = id { | 68 | if err.to_string() == "io: No such file or directory (os error 2)" { |
75 | list.iter() | 69 | return Ok(None); |
76 | .find(|bl| bl.id == id) | 70 | }; |
77 | .ok_or(Error::BackupNotFound)? | 71 | return Err(err); |
78 | .rel_location | 72 | } |
79 | .clone() | ||
80 | } else { | ||
81 | list.last() | ||
82 | .ok_or(Error::BackupNotFound)? | ||
83 | .rel_location | ||
84 | .clone() | ||
85 | }; | 73 | }; |
86 | 74 | ||
75 | Ok(Some(Self::from_index( | ||
76 | config, | ||
77 | list.last().ok_or(Error::BackupNotFound)?.id.clone(), | ||
78 | )?)) | ||
79 | } | ||
80 | |||
81 | pub fn from_index(config: &Config, id: BackupId) -> Result<Self> { | ||
82 | let backup_index_root = format!("{}/index.json", config.root); | ||
83 | let list: Vec<BackupLocation> = Self::get_json_content(&backup_index_root)?; | ||
84 | let index_loc = list | ||
85 | .iter() | ||
86 | .find(|bl| bl.id == id) | ||
87 | .ok_or(Error::BackupNotFound)? | ||
88 | .rel_location | ||
89 | .clone(); | ||
90 | |||
87 | let path = format!("{}/{index_loc}/index.json", config.root); | 91 | let path = format!("{}/{index_loc}/index.json", config.root); |
88 | let index_file: Self = Self::get_json_content(&path)?; | 92 | let index_file: Self = Self::get_json_content(&path)?; |
89 | 93 | ||
90 | Ok(index_file) | 94 | Ok(index_file) |
91 | } | 95 | } |
92 | 96 | ||
97 | pub fn get_location(&self, config: &Config) -> BackupLocation { | ||
98 | let rel_location = format!("{}_{}", config.device, self.timestamp); | ||
99 | |||
100 | BackupLocation { | ||
101 | id: self.id.to_string(), | ||
102 | rel_location, | ||
103 | } | ||
104 | } | ||
105 | |||
106 | pub fn get_absolute_file_location(&self, config: &Config, rel_location: &str) -> String { | ||
107 | let loc = self.get_location(config).get_absolute_dir(config); | ||
108 | |||
109 | format!("{}/{}", loc, rel_location) | ||
110 | } | ||
111 | |||
93 | fn get_json_content<T: for<'a> Deserialize<'a>>(path: &str) -> Result<T> { | 112 | fn get_json_content<T: for<'a> Deserialize<'a>>(path: &str) -> Result<T> { |
94 | let mut file = File::open(path)?; | 113 | let mut file = File::open(path)?; |
95 | let mut content = String::new(); | 114 | let mut content = String::new(); |
@@ -97,7 +116,26 @@ impl Backup { | |||
97 | Ok(serde_json::from_str(&content)?) | 116 | Ok(serde_json::from_str(&content)?) |
98 | } | 117 | } |
99 | 118 | ||
100 | fn append_to_root_index(config: &Config, new_backup: BackupLocation) -> Result<()> { | 119 | fn get_timestamp() -> u64 { |
120 | SystemTime::now() | ||
121 | .duration_since(UNIX_EPOCH) | ||
122 | .unwrap() | ||
123 | .as_secs() | ||
124 | } | ||
125 | } | ||
126 | |||
127 | #[derive(Debug, Clone, Serialize, Deserialize)] | ||
128 | pub struct BackupLocation { | ||
129 | id: BackupId, | ||
130 | rel_location: String, | ||
131 | } | ||
132 | |||
133 | impl BackupLocation { | ||
134 | pub fn get_absolute_dir(&self, config: &Config) -> String { | ||
135 | format!("{}/{}", config.root, self.rel_location) | ||
136 | } | ||
137 | |||
138 | pub fn append_to_root(&self, config: &Config) -> Result<()> { | ||
101 | let backup_index_root = format!("{}/index.json", config.root); | 139 | let backup_index_root = format!("{}/index.json", config.root); |
102 | let path = PathBuf::from(&backup_index_root); | 140 | let path = PathBuf::from(&backup_index_root); |
103 | if path.exists() { | 141 | if path.exists() { |
@@ -107,27 +145,15 @@ impl Backup { | |||
107 | let mut loc: Vec<BackupLocation> = serde_json::from_str(&content)?; | 145 | let mut loc: Vec<BackupLocation> = serde_json::from_str(&content)?; |
108 | 146 | ||
109 | let mut f = File::create(path)?; | 147 | let mut f = File::create(path)?; |
110 | loc.push(new_backup); | 148 | loc.push(self.clone()); |
111 | 149 | ||
112 | f.write_all(&serde_json::to_vec(&loc)?)?; | 150 | f.write_all(&serde_json::to_vec(&loc)?)?; |
113 | } else { | 151 | } else { |
152 | create_dir_all(&config.root).unwrap(); | ||
114 | let mut f = File::create(backup_index_root)?; | 153 | let mut f = File::create(backup_index_root)?; |
115 | f.write_all(&serde_json::to_vec(&vec![new_backup])?)?; | 154 | f.write_all(&serde_json::to_vec(&vec![self])?)?; |
116 | }; | 155 | }; |
117 | 156 | ||
118 | Ok(()) | 157 | Ok(()) |
119 | } | 158 | } |
120 | |||
121 | fn get_timestamp() -> u64 { | ||
122 | SystemTime::now() | ||
123 | .duration_since(UNIX_EPOCH) | ||
124 | .unwrap() | ||
125 | .as_secs() | ||
126 | } | ||
127 | } | ||
128 | |||
129 | #[derive(Debug, Clone, Serialize, Deserialize)] | ||
130 | struct BackupLocation { | ||
131 | id: BackupId, | ||
132 | rel_location: String, | ||
133 | } | 159 | } |