diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backup.rs | 89 | ||||
-rw-r--r-- | src/error.rs | 17 | ||||
-rw-r--r-- | src/main.rs | 13 | ||||
-rw-r--r-- | src/pathinfo.rs | 188 | ||||
-rw-r--r-- | src/storage.rs | 8 |
5 files changed, 197 insertions, 118 deletions
diff --git a/src/backup.rs b/src/backup.rs index 4e74c97..69bc2ea 100644 --- a/src/backup.rs +++ b/src/backup.rs | |||
@@ -1,9 +1,20 @@ | |||
1 | use std::time::{SystemTime, UNIX_EPOCH}; | 1 | use std::{ |
2 | fs::{create_dir_all, File, OpenOptions}, | ||
3 | io::{ErrorKind, Read, Write}, | ||
4 | path::PathBuf, | ||
5 | time::{SystemTime, UNIX_EPOCH}, | ||
6 | }; | ||
2 | 7 | ||
8 | use gethostname::gethostname; | ||
3 | use serde::{Deserialize, Serialize}; | 9 | use serde::{Deserialize, Serialize}; |
4 | use uuid::Uuid; | 10 | use uuid::Uuid; |
5 | 11 | ||
6 | use crate::{config::Config, pathinfo::PathInfo, packages::Package, error::Result}; | 12 | use crate::{ |
13 | config::Config, | ||
14 | error::{Error, Result}, | ||
15 | packages::Package, | ||
16 | pathinfo::PathInfo, | ||
17 | }; | ||
7 | 18 | ||
8 | pub type BackupId = String; | 19 | pub type BackupId = String; |
9 | 20 | ||
@@ -24,21 +35,81 @@ impl Backup { | |||
24 | Ok(Self { | 35 | Ok(Self { |
25 | // UUID not really needed, maybe a shorter hash | 36 | // UUID not really needed, maybe a shorter hash |
26 | id: Uuid::new_v4().to_string(), | 37 | id: Uuid::new_v4().to_string(), |
27 | timestamp: SystemTime::now() | 38 | timestamp: Self::get_timestamp(), |
28 | .duration_since(UNIX_EPOCH) | ||
29 | .unwrap() | ||
30 | .as_secs(), | ||
31 | packages, | 39 | packages, |
32 | files, | 40 | files, |
33 | }) | 41 | }) |
34 | } | 42 | } |
35 | 43 | ||
36 | 44 | pub fn save(&self, config: &Config) -> Result<()> { | |
45 | let rel_location = format!( | ||
46 | "bu_{}_{}", | ||
47 | gethostname() | ||
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 | |||
60 | let backup_root = format!("{}/{}", config.root, bl.rel_location); | ||
61 | create_dir_all(&backup_root).unwrap(); | ||
62 | let path = format!("{}/index.json", backup_root); | ||
63 | let mut f = File::create(path).unwrap(); | ||
64 | f.write_all(&serde_json::to_vec(self).unwrap()).unwrap(); | ||
65 | |||
66 | Ok(()) | ||
67 | } | ||
68 | |||
69 | pub fn get(config: &Config, _id: Option<BackupId>) -> Result<()> { | ||
70 | let backup_index_root = format!("{}/index.json", config.root); | ||
71 | let mut file = File::open(backup_index_root)?; | ||
72 | let mut content = String::new(); | ||
73 | file.read_to_string(&mut content)?; | ||
74 | let list: Vec<BackupLocation> = serde_json::from_str(&content)?; | ||
75 | println!("{list:#?}"); | ||
76 | |||
77 | todo!(); | ||
78 | |||
79 | Ok(()) | ||
80 | } | ||
81 | |||
82 | fn append_to_root_index(config: &Config, new_backup: BackupLocation) -> Result<()> { | ||
83 | let backup_index_root = format!("{}/index.json", config.root); | ||
84 | let path = PathBuf::from(&backup_index_root); | ||
85 | if path.exists() { | ||
86 | let mut f = File::open(&path)?; | ||
87 | let mut content = String::new(); | ||
88 | f.read_to_string(&mut content)?; | ||
89 | let mut loc: Vec<BackupLocation> = serde_json::from_str(&content)?; | ||
90 | |||
91 | let mut f = File::create(path)?; | ||
92 | loc.push(new_backup); | ||
93 | |||
94 | f.write_all(&serde_json::to_vec(&loc)?)?; | ||
95 | } else { | ||
96 | let mut f = File::create(backup_index_root)?; | ||
97 | f.write_all(&serde_json::to_vec(&vec![new_backup])?)?; | ||
98 | }; | ||
99 | |||
100 | Ok(()) | ||
101 | } | ||
102 | |||
103 | fn get_timestamp() -> u64 { | ||
104 | SystemTime::now() | ||
105 | .duration_since(UNIX_EPOCH) | ||
106 | .unwrap() | ||
107 | .as_secs() | ||
108 | } | ||
37 | } | 109 | } |
38 | 110 | ||
111 | #[derive(Debug, Clone, Serialize, Deserialize)] | ||
39 | struct BackupLocation { | 112 | struct BackupLocation { |
40 | id: BackupId, | 113 | id: BackupId, |
41 | rel_location: String, | 114 | rel_location: String, |
42 | } | 115 | } |
43 | |||
44 | type BackupList = Vec<BackupLocation>; | ||
diff --git a/src/error.rs b/src/error.rs index 77eab69..dc132f4 100644 --- a/src/error.rs +++ b/src/error.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | pub type Result<T> = std::result::Result<T, Error>; | 1 | pub type Result<T> = std::result::Result<T, Error>; |
2 | 2 | ||
3 | #[derive(Debug, PartialEq, Eq, thiserror::Error)] | 3 | #[derive(Debug, thiserror::Error)] |
4 | pub enum Error { | 4 | pub enum Error { |
5 | #[error("unknown custom directory '{0}'")] | 5 | #[error("unknown custom directory '{0}'")] |
6 | CustomDirectory(String), | 6 | CustomDirectory(String), |
@@ -16,4 +16,19 @@ pub enum Error { | |||
16 | 16 | ||
17 | #[error("Only exactly one user allowed in config")] | 17 | #[error("Only exactly one user allowed in config")] |
18 | MultiUser, | 18 | MultiUser, |
19 | |||
20 | #[error("OsString couldn't be converted to string")] | ||
21 | InvalidOsString, | ||
22 | |||
23 | #[error("json: {source}")] | ||
24 | SerdeJson { | ||
25 | #[from] | ||
26 | source: serde_json::Error, | ||
27 | }, | ||
28 | |||
29 | #[error("io: {source}")] | ||
30 | Io { | ||
31 | #[from] | ||
32 | source: std::io::Error, | ||
33 | } | ||
19 | } | 34 | } |
diff --git a/src/main.rs b/src/main.rs index 1fdcebf..e0b3758 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,22 +1,21 @@ | |||
1 | use backup::Backup; | 1 | use backup::Backup; |
2 | use config::Config; | 2 | use config::Config; |
3 | use packages::{pacman::Pacman, PackageManager}; | 3 | use packages::{pacman::Pacman, PackageManager}; |
4 | use storage::save_index; | ||
5 | 4 | ||
6 | mod backup; | 5 | mod backup; |
7 | mod config; | 6 | mod config; |
8 | mod error; | 7 | mod error; |
9 | mod pathinfo; | 8 | mod pathinfo; |
10 | mod packages; | 9 | mod packages; |
11 | mod storage; | ||
12 | 10 | ||
13 | fn main() -> anyhow::Result<()> { | 11 | fn main() -> color_eyre::Result<()> { |
12 | color_eyre::install()?; | ||
13 | |||
14 | let mut cfg = Config::load()?; | 14 | let mut cfg = Config::load()?; |
15 | cfg.user.push("fx".to_string()); | 15 | cfg.user.push("fx".to_string()); |
16 | cfg.directories.push("~/.config/nvim".to_string()); | 16 | cfg.directories.push("~/.config/nvim".to_string()); |
17 | cfg.directories.push("~/.config/hypr".to_string()); | 17 | cfg.directories.push("~/.config/hypr".to_string()); |
18 | let toml = toml::to_string(&cfg)?; | 18 | cfg.root = "./backup".to_string(); |
19 | println!("{toml}"); | ||
20 | 19 | ||
21 | let pacman = Pacman; | 20 | let pacman = Pacman; |
22 | let pkgs = pacman.get_installed(); | 21 | let pkgs = pacman.get_installed(); |
@@ -24,7 +23,9 @@ fn main() -> anyhow::Result<()> { | |||
24 | let backup = Backup::create(&cfg, pkgs)?; | 23 | let backup = Backup::create(&cfg, pkgs)?; |
25 | // println!("{backup:#?}"); | 24 | // println!("{backup:#?}"); |
26 | 25 | ||
27 | save_index(backup); | 26 | backup.save(&cfg)?; |
27 | |||
28 | Backup::get(&cfg, None)?; | ||
28 | 29 | ||
29 | // let fi = FileInfo::new("~/.config/nvim", &cfg)?; | 30 | // let fi = FileInfo::new("~/.config/nvim", &cfg)?; |
30 | // println!("{:?}", fi.get_absolute_path()); | 31 | // println!("{:?}", fi.get_absolute_path()); |
diff --git a/src/pathinfo.rs b/src/pathinfo.rs index b0c3be4..be43b6e 100644 --- a/src/pathinfo.rs +++ b/src/pathinfo.rs | |||
@@ -150,97 +150,97 @@ impl LocationRoot { | |||
150 | } | 150 | } |
151 | } | 151 | } |
152 | 152 | ||
153 | #[cfg(test)] | 153 | // #[cfg(test)] |
154 | mod tests { | 154 | // mod tests { |
155 | use crate::{ | 155 | // use crate::{ |
156 | config::Config, | 156 | // config::Config, |
157 | error::{Error, Result}, | 157 | // error::{Error, Result}, |
158 | pathinfo::PathInfo, | 158 | // pathinfo::PathInfo, |
159 | }; | 159 | // }; |
160 | 160 | // | |
161 | use super::LocationRoot; | 161 | // use super::LocationRoot; |
162 | 162 | // | |
163 | #[test] | 163 | // #[test] |
164 | fn from_op_str() -> Result<()> { | 164 | // fn from_op_str() -> Result<()> { |
165 | let mut config = Config::default(); | 165 | // let mut config = Config::default(); |
166 | config | 166 | // config |
167 | .custom_directories | 167 | // .custom_directories |
168 | .insert("test".to_string(), "/usr/local/test".to_string()); | 168 | // .insert("test".to_string(), "/usr/local/test".to_string()); |
169 | 169 | // | |
170 | let mut values: Vec<(&str, Result<LocationRoot>)> = Vec::new(); | 170 | // let mut values: Vec<(&str, Result<LocationRoot>)> = Vec::new(); |
171 | values.push(("u:test", Ok(LocationRoot::User("test".to_string())))); | 171 | // values.push(("u:test", Ok(LocationRoot::User("test".to_string())))); |
172 | values.push(("s:", Ok(LocationRoot::SystemSettings))); | 172 | // values.push(("s:", Ok(LocationRoot::SystemSettings))); |
173 | values.push(("r:", Ok(LocationRoot::Root))); | 173 | // values.push(("r:", Ok(LocationRoot::Root))); |
174 | values.push(( | 174 | // values.push(( |
175 | "c:test", | 175 | // "c:test", |
176 | Ok(LocationRoot::Custom("/usr/local/test".to_string())), | 176 | // Ok(LocationRoot::Custom("/usr/local/test".to_string())), |
177 | )); | 177 | // )); |
178 | values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string())))); | 178 | // values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string())))); |
179 | values.push(("t:test/", Err(Error::InvalidIndex("t".to_string())))); | 179 | // values.push(("t:test/", Err(Error::InvalidIndex("t".to_string())))); |
180 | values.push(( | 180 | // values.push(( |
181 | "test:test/usr", | 181 | // "test:test/usr", |
182 | Err(Error::InvalidIndex("test".to_string())), | 182 | // Err(Error::InvalidIndex("test".to_string())), |
183 | )); | 183 | // )); |
184 | values.push(("/usr/local/test", Err(Error::NoIndex))); | 184 | // values.push(("/usr/local/test", Err(Error::NoIndex))); |
185 | values.push(("c/usr/local/test", Err(Error::NoIndex))); | 185 | // values.push(("c/usr/local/test", Err(Error::NoIndex))); |
186 | 186 | // | |
187 | for value in values { | 187 | // for value in values { |
188 | print!("Testing {value:?}"); | 188 | // print!("Testing {value:?}"); |
189 | assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1); | 189 | // assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1); |
190 | println!("\rTesting {value:?} ✓"); | 190 | // println!("\rTesting {value:?} ✓"); |
191 | } | 191 | // } |
192 | 192 | // | |
193 | Ok(()) | 193 | // Ok(()) |
194 | } | 194 | // } |
195 | 195 | // | |
196 | #[test] | 196 | // #[test] |
197 | fn parse_location() -> Result<()> { | 197 | // fn parse_location() -> Result<()> { |
198 | let mut config = Config::default(); | 198 | // let mut config = Config::default(); |
199 | config.user.push("test".to_string()); | 199 | // config.user.push("test".to_string()); |
200 | config | 200 | // config |
201 | .custom_directories | 201 | // .custom_directories |
202 | .insert("test".to_string(), "/usr/local/test".to_string()); | 202 | // .insert("test".to_string(), "/usr/local/test".to_string()); |
203 | 203 | // | |
204 | let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new(); | 204 | // let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new(); |
205 | values.push(( | 205 | // values.push(( |
206 | "~/.config/nvim", | 206 | // "~/.config/nvim", |
207 | Ok(( | 207 | // Ok(( |
208 | ".config/nvim".to_string(), | 208 | // ".config/nvim".to_string(), |
209 | LocationRoot::User("test".to_string()), | 209 | // LocationRoot::User("test".to_string()), |
210 | )), | 210 | // )), |
211 | )); | 211 | // )); |
212 | values.push(( | 212 | // values.push(( |
213 | "u:test/.config/nvim", | 213 | // "u:test/.config/nvim", |
214 | Ok(( | 214 | // Ok(( |
215 | ".config/nvim".to_string(), | 215 | // ".config/nvim".to_string(), |
216 | LocationRoot::User("test".to_string()), | 216 | // LocationRoot::User("test".to_string()), |
217 | )), | 217 | // )), |
218 | )); | 218 | // )); |
219 | values.push(( | 219 | // values.push(( |
220 | "r:/.config/nvim", | 220 | // "r:/.config/nvim", |
221 | Ok((".config/nvim".to_string(), LocationRoot::Root)), | 221 | // Ok((".config/nvim".to_string(), LocationRoot::Root)), |
222 | )); | 222 | // )); |
223 | values.push(( | 223 | // values.push(( |
224 | "r:/.config/nvim", | 224 | // "r:/.config/nvim", |
225 | Ok((".config/nvim".to_string(), LocationRoot::Root)), | 225 | // Ok((".config/nvim".to_string(), LocationRoot::Root)), |
226 | )); | 226 | // )); |
227 | values.push(( | 227 | // values.push(( |
228 | "s:/.config/nvim", | 228 | // "s:/.config/nvim", |
229 | Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)), | 229 | // Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)), |
230 | )); | 230 | // )); |
231 | values.push(( | 231 | // values.push(( |
232 | "c:test/.config/nvim", | 232 | // "c:test/.config/nvim", |
233 | Ok(( | 233 | // Ok(( |
234 | ".config/nvim".to_string(), | 234 | // ".config/nvim".to_string(), |
235 | LocationRoot::Custom("/usr/local/test".to_string()), | 235 | // LocationRoot::Custom("/usr/local/test".to_string()), |
236 | )), | 236 | // )), |
237 | )); | 237 | // )); |
238 | 238 | // | |
239 | for value in values { | 239 | // for value in values { |
240 | print!("Testing {value:?}"); | 240 | // print!("Testing {value:?}"); |
241 | assert_eq!(PathInfo::parse_location(&value.0, &config), value.1); | 241 | // assert_eq!(PathInfo::parse_location(&value.0, &config), value.1); |
242 | println!("\rTesting {value:?} ✓"); | 242 | // println!("\rTesting {value:?} ✓"); |
243 | } | 243 | // } |
244 | Ok(()) | 244 | // Ok(()) |
245 | } | 245 | // } |
246 | } | 246 | // } |
diff --git a/src/storage.rs b/src/storage.rs deleted file mode 100644 index b9e8de9..0000000 --- a/src/storage.rs +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | use std::{fs::File, io::Write}; | ||
2 | |||
3 | use crate::backup::Backup; | ||
4 | |||
5 | pub fn save_index(backup: Backup) { | ||
6 | let mut f = File::create("./index.json").unwrap(); | ||
7 | f.write_all(&serde_json::to_vec(&backup).unwrap()).unwrap(); | ||
8 | } | ||