diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Cargo.lock | 564 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | src/backup.rs | 44 | ||||
-rw-r--r-- | src/config.rs | 33 | ||||
-rw-r--r-- | src/error.rs | 19 | ||||
-rw-r--r-- | src/main.rs | 32 | ||||
-rw-r--r-- | src/packages.rs | 16 | ||||
-rw-r--r-- | src/packages/pacman.rs | 46 | ||||
-rw-r--r-- | src/pathinfo.rs | 246 | ||||
-rw-r--r-- | src/storage.rs | 8 |
11 files changed, 1022 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1 @@ | |||
/target | |||
diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ef301aa --- /dev/null +++ b/Cargo.lock | |||
@@ -0,0 +1,564 @@ | |||
1 | # This file is automatically @generated by Cargo. | ||
2 | # It is not intended for manual editing. | ||
3 | version = 3 | ||
4 | |||
5 | [[package]] | ||
6 | name = "anyhow" | ||
7 | version = "1.0.86" | ||
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
9 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" | ||
10 | |||
11 | [[package]] | ||
12 | name = "async-trait" | ||
13 | version = "0.1.82" | ||
14 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
15 | checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" | ||
16 | dependencies = [ | ||
17 | "proc-macro2", | ||
18 | "quote", | ||
19 | "syn", | ||
20 | ] | ||
21 | |||
22 | [[package]] | ||
23 | name = "base64" | ||
24 | version = "0.21.7" | ||
25 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
26 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" | ||
27 | |||
28 | [[package]] | ||
29 | name = "bitflags" | ||
30 | version = "2.6.0" | ||
31 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
32 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" | ||
33 | dependencies = [ | ||
34 | "serde", | ||
35 | ] | ||
36 | |||
37 | [[package]] | ||
38 | name = "block-buffer" | ||
39 | version = "0.10.4" | ||
40 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
41 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" | ||
42 | dependencies = [ | ||
43 | "generic-array", | ||
44 | ] | ||
45 | |||
46 | [[package]] | ||
47 | name = "cfg-if" | ||
48 | version = "1.0.0" | ||
49 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
50 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||
51 | |||
52 | [[package]] | ||
53 | name = "config" | ||
54 | version = "0.14.0" | ||
55 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
56 | checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" | ||
57 | dependencies = [ | ||
58 | "async-trait", | ||
59 | "convert_case", | ||
60 | "json5", | ||
61 | "lazy_static", | ||
62 | "nom", | ||
63 | "pathdiff", | ||
64 | "ron", | ||
65 | "rust-ini", | ||
66 | "serde", | ||
67 | "serde_json", | ||
68 | "toml", | ||
69 | "yaml-rust", | ||
70 | ] | ||
71 | |||
72 | [[package]] | ||
73 | name = "const-random" | ||
74 | version = "0.1.18" | ||
75 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
76 | checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" | ||
77 | dependencies = [ | ||
78 | "const-random-macro", | ||
79 | ] | ||
80 | |||
81 | [[package]] | ||
82 | name = "const-random-macro" | ||
83 | version = "0.1.16" | ||
84 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
85 | checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" | ||
86 | dependencies = [ | ||
87 | "getrandom", | ||
88 | "once_cell", | ||
89 | "tiny-keccak", | ||
90 | ] | ||
91 | |||
92 | [[package]] | ||
93 | name = "convert_case" | ||
94 | version = "0.6.0" | ||
95 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
96 | checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" | ||
97 | dependencies = [ | ||
98 | "unicode-segmentation", | ||
99 | ] | ||
100 | |||
101 | [[package]] | ||
102 | name = "cpufeatures" | ||
103 | version = "0.2.13" | ||
104 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
105 | checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" | ||
106 | dependencies = [ | ||
107 | "libc", | ||
108 | ] | ||
109 | |||
110 | [[package]] | ||
111 | name = "crunchy" | ||
112 | version = "0.2.2" | ||
113 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
114 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" | ||
115 | |||
116 | [[package]] | ||
117 | name = "crypto-common" | ||
118 | version = "0.1.6" | ||
119 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
120 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" | ||
121 | dependencies = [ | ||
122 | "generic-array", | ||
123 | "typenum", | ||
124 | ] | ||
125 | |||
126 | [[package]] | ||
127 | name = "digest" | ||
128 | version = "0.10.7" | ||
129 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
130 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" | ||
131 | dependencies = [ | ||
132 | "block-buffer", | ||
133 | "crypto-common", | ||
134 | ] | ||
135 | |||
136 | [[package]] | ||
137 | name = "dlv-list" | ||
138 | version = "0.5.2" | ||
139 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
140 | checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" | ||
141 | dependencies = [ | ||
142 | "const-random", | ||
143 | ] | ||
144 | |||
145 | [[package]] | ||
146 | name = "equivalent" | ||
147 | version = "1.0.1" | ||
148 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
149 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||
150 | |||
151 | [[package]] | ||
152 | name = "fxbaup" | ||
153 | version = "0.1.0" | ||
154 | dependencies = [ | ||
155 | "anyhow", | ||
156 | "config", | ||
157 | "serde", | ||
158 | "serde_json", | ||
159 | "thiserror", | ||
160 | "toml", | ||
161 | "uuid", | ||
162 | ] | ||
163 | |||
164 | [[package]] | ||
165 | name = "generic-array" | ||
166 | version = "0.14.7" | ||
167 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
168 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" | ||
169 | dependencies = [ | ||
170 | "typenum", | ||
171 | "version_check", | ||
172 | ] | ||
173 | |||
174 | [[package]] | ||
175 | name = "getrandom" | ||
176 | version = "0.2.15" | ||
177 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
178 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" | ||
179 | dependencies = [ | ||
180 | "cfg-if", | ||
181 | "libc", | ||
182 | "wasi", | ||
183 | ] | ||
184 | |||
185 | [[package]] | ||
186 | name = "hashbrown" | ||
187 | version = "0.13.2" | ||
188 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
189 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" | ||
190 | |||
191 | [[package]] | ||
192 | name = "hashbrown" | ||
193 | version = "0.14.5" | ||
194 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
195 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" | ||
196 | |||
197 | [[package]] | ||
198 | name = "indexmap" | ||
199 | version = "2.5.0" | ||
200 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
201 | checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" | ||
202 | dependencies = [ | ||
203 | "equivalent", | ||
204 | "hashbrown 0.14.5", | ||
205 | ] | ||
206 | |||
207 | [[package]] | ||
208 | name = "itoa" | ||
209 | version = "1.0.11" | ||
210 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
211 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" | ||
212 | |||
213 | [[package]] | ||
214 | name = "json5" | ||
215 | version = "0.4.1" | ||
216 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
217 | checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" | ||
218 | dependencies = [ | ||
219 | "pest", | ||
220 | "pest_derive", | ||
221 | "serde", | ||
222 | ] | ||
223 | |||
224 | [[package]] | ||
225 | name = "lazy_static" | ||
226 | version = "1.5.0" | ||
227 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
228 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" | ||
229 | |||
230 | [[package]] | ||
231 | name = "libc" | ||
232 | version = "0.2.158" | ||
233 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
234 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" | ||
235 | |||
236 | [[package]] | ||
237 | name = "linked-hash-map" | ||
238 | version = "0.5.6" | ||
239 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
240 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" | ||
241 | |||
242 | [[package]] | ||
243 | name = "memchr" | ||
244 | version = "2.7.4" | ||
245 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
246 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | ||
247 | |||
248 | [[package]] | ||
249 | name = "minimal-lexical" | ||
250 | version = "0.2.1" | ||
251 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
252 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" | ||
253 | |||
254 | [[package]] | ||
255 | name = "nom" | ||
256 | version = "7.1.3" | ||
257 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
258 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" | ||
259 | dependencies = [ | ||
260 | "memchr", | ||
261 | "minimal-lexical", | ||
262 | ] | ||
263 | |||
264 | [[package]] | ||
265 | name = "once_cell" | ||
266 | version = "1.19.0" | ||
267 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
268 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||
269 | |||
270 | [[package]] | ||
271 | name = "ordered-multimap" | ||
272 | version = "0.6.0" | ||
273 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
274 | checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" | ||
275 | dependencies = [ | ||
276 | "dlv-list", | ||
277 | "hashbrown 0.13.2", | ||
278 | ] | ||
279 | |||
280 | [[package]] | ||
281 | name = "pathdiff" | ||
282 | version = "0.2.1" | ||
283 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
284 | checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" | ||
285 | |||
286 | [[package]] | ||
287 | name = "pest" | ||
288 | version = "2.7.11" | ||
289 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
290 | checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" | ||
291 | dependencies = [ | ||
292 | "memchr", | ||
293 | "thiserror", | ||
294 | "ucd-trie", | ||
295 | ] | ||
296 | |||
297 | [[package]] | ||
298 | name = "pest_derive" | ||
299 | version = "2.7.11" | ||
300 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
301 | checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" | ||
302 | dependencies = [ | ||
303 | "pest", | ||
304 | "pest_generator", | ||
305 | ] | ||
306 | |||
307 | [[package]] | ||
308 | name = "pest_generator" | ||
309 | version = "2.7.11" | ||
310 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
311 | checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" | ||
312 | dependencies = [ | ||
313 | "pest", | ||
314 | "pest_meta", | ||
315 | "proc-macro2", | ||
316 | "quote", | ||
317 | "syn", | ||
318 | ] | ||
319 | |||
320 | [[package]] | ||
321 | name = "pest_meta" | ||
322 | version = "2.7.11" | ||
323 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
324 | checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" | ||
325 | dependencies = [ | ||
326 | "once_cell", | ||
327 | "pest", | ||
328 | "sha2", | ||
329 | ] | ||
330 | |||
331 | [[package]] | ||
332 | name = "proc-macro2" | ||
333 | version = "1.0.86" | ||
334 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
335 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" | ||
336 | dependencies = [ | ||
337 | "unicode-ident", | ||
338 | ] | ||
339 | |||
340 | [[package]] | ||
341 | name = "quote" | ||
342 | version = "1.0.37" | ||
343 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
344 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" | ||
345 | dependencies = [ | ||
346 | "proc-macro2", | ||
347 | ] | ||
348 | |||
349 | [[package]] | ||
350 | name = "ron" | ||
351 | version = "0.8.1" | ||
352 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
353 | checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" | ||
354 | dependencies = [ | ||
355 | "base64", | ||
356 | "bitflags", | ||
357 | "serde", | ||
358 | "serde_derive", | ||
359 | ] | ||
360 | |||
361 | [[package]] | ||
362 | name = "rust-ini" | ||
363 | version = "0.19.0" | ||
364 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
365 | checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" | ||
366 | dependencies = [ | ||
367 | "cfg-if", | ||
368 | "ordered-multimap", | ||
369 | ] | ||
370 | |||
371 | [[package]] | ||
372 | name = "ryu" | ||
373 | version = "1.0.18" | ||
374 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
375 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" | ||
376 | |||
377 | [[package]] | ||
378 | name = "serde" | ||
379 | version = "1.0.209" | ||
380 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
381 | checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" | ||
382 | dependencies = [ | ||
383 | "serde_derive", | ||
384 | ] | ||
385 | |||
386 | [[package]] | ||
387 | name = "serde_derive" | ||
388 | version = "1.0.209" | ||
389 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
390 | checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" | ||
391 | dependencies = [ | ||
392 | "proc-macro2", | ||
393 | "quote", | ||
394 | "syn", | ||
395 | ] | ||
396 | |||
397 | [[package]] | ||
398 | name = "serde_json" | ||
399 | version = "1.0.128" | ||
400 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
401 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" | ||
402 | dependencies = [ | ||
403 | "itoa", | ||
404 | "memchr", | ||
405 | "ryu", | ||
406 | "serde", | ||
407 | ] | ||
408 | |||
409 | [[package]] | ||
410 | name = "serde_spanned" | ||
411 | version = "0.6.7" | ||
412 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
413 | checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" | ||
414 | dependencies = [ | ||
415 | "serde", | ||
416 | ] | ||
417 | |||
418 | [[package]] | ||
419 | name = "sha2" | ||
420 | version = "0.10.8" | ||
421 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
422 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" | ||
423 | dependencies = [ | ||
424 | "cfg-if", | ||
425 | "cpufeatures", | ||
426 | "digest", | ||
427 | ] | ||
428 | |||
429 | [[package]] | ||
430 | name = "syn" | ||
431 | version = "2.0.77" | ||
432 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
433 | checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" | ||
434 | dependencies = [ | ||
435 | "proc-macro2", | ||
436 | "quote", | ||
437 | "unicode-ident", | ||
438 | ] | ||
439 | |||
440 | [[package]] | ||
441 | name = "thiserror" | ||
442 | version = "1.0.63" | ||
443 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
444 | checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" | ||
445 | dependencies = [ | ||
446 | "thiserror-impl", | ||
447 | ] | ||
448 | |||
449 | [[package]] | ||
450 | name = "thiserror-impl" | ||
451 | version = "1.0.63" | ||
452 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
453 | checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" | ||
454 | dependencies = [ | ||
455 | "proc-macro2", | ||
456 | "quote", | ||
457 | "syn", | ||
458 | ] | ||
459 | |||
460 | [[package]] | ||
461 | name = "tiny-keccak" | ||
462 | version = "2.0.2" | ||
463 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
464 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" | ||
465 | dependencies = [ | ||
466 | "crunchy", | ||
467 | ] | ||
468 | |||
469 | [[package]] | ||
470 | name = "toml" | ||
471 | version = "0.8.19" | ||
472 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
473 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" | ||
474 | dependencies = [ | ||
475 | "serde", | ||
476 | "serde_spanned", | ||
477 | "toml_datetime", | ||
478 | "toml_edit", | ||
479 | ] | ||
480 | |||
481 | [[package]] | ||
482 | name = "toml_datetime" | ||
483 | version = "0.6.8" | ||
484 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
485 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" | ||
486 | dependencies = [ | ||
487 | "serde", | ||
488 | ] | ||
489 | |||
490 | [[package]] | ||
491 | name = "toml_edit" | ||
492 | version = "0.22.20" | ||
493 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
494 | checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" | ||
495 | dependencies = [ | ||
496 | "indexmap", | ||
497 | "serde", | ||
498 | "serde_spanned", | ||
499 | "toml_datetime", | ||
500 | "winnow", | ||
501 | ] | ||
502 | |||
503 | [[package]] | ||
504 | name = "typenum" | ||
505 | version = "1.17.0" | ||
506 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
507 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" | ||
508 | |||
509 | [[package]] | ||
510 | name = "ucd-trie" | ||
511 | version = "0.1.6" | ||
512 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
513 | checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" | ||
514 | |||
515 | [[package]] | ||
516 | name = "unicode-ident" | ||
517 | version = "1.0.12" | ||
518 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
519 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | ||
520 | |||
521 | [[package]] | ||
522 | name = "unicode-segmentation" | ||
523 | version = "1.11.0" | ||
524 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
525 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" | ||
526 | |||
527 | [[package]] | ||
528 | name = "uuid" | ||
529 | version = "1.10.0" | ||
530 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
531 | checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" | ||
532 | dependencies = [ | ||
533 | "getrandom", | ||
534 | ] | ||
535 | |||
536 | [[package]] | ||
537 | name = "version_check" | ||
538 | version = "0.9.5" | ||
539 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
540 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" | ||
541 | |||
542 | [[package]] | ||
543 | name = "wasi" | ||
544 | version = "0.11.0+wasi-snapshot-preview1" | ||
545 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
546 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||
547 | |||
548 | [[package]] | ||
549 | name = "winnow" | ||
550 | version = "0.6.18" | ||
551 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
552 | checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" | ||
553 | dependencies = [ | ||
554 | "memchr", | ||
555 | ] | ||
556 | |||
557 | [[package]] | ||
558 | name = "yaml-rust" | ||
559 | version = "0.4.5" | ||
560 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
561 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" | ||
562 | dependencies = [ | ||
563 | "linked-hash-map", | ||
564 | ] | ||
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7057f7a --- /dev/null +++ b/Cargo.toml | |||
@@ -0,0 +1,13 @@ | |||
1 | [package] | ||
2 | name = "fxbaup" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | anyhow = "1.0.86" | ||
8 | config = "0.14.0" | ||
9 | serde = { version = "1.0.209", features = ["derive"] } | ||
10 | serde_json = "1.0.128" | ||
11 | thiserror = "1.0.63" | ||
12 | toml = "0.8.19" | ||
13 | uuid = { version = "1.10.0", features = ["v4"] } | ||
diff --git a/src/backup.rs b/src/backup.rs new file mode 100644 index 0000000..4e74c97 --- /dev/null +++ b/src/backup.rs | |||
@@ -0,0 +1,44 @@ | |||
1 | use std::time::{SystemTime, UNIX_EPOCH}; | ||
2 | |||
3 | use serde::{Deserialize, Serialize}; | ||
4 | use uuid::Uuid; | ||
5 | |||
6 | use crate::{config::Config, pathinfo::PathInfo, packages::Package, error::Result}; | ||
7 | |||
8 | pub type BackupId = String; | ||
9 | |||
10 | #[derive(Debug, Serialize, Deserialize)] | ||
11 | pub struct Backup { | ||
12 | id: String, | ||
13 | timestamp: u64, | ||
14 | packages: Vec<Package>, | ||
15 | files: Vec<PathInfo>, | ||
16 | } | ||
17 | |||
18 | impl Backup { | ||
19 | pub fn create(config: &Config, packages: Vec<Package>) -> Result<Self> { | ||
20 | let mut files: Vec<PathInfo> = Vec::new(); | ||
21 | for dir in &config.directories { | ||
22 | files.push(PathInfo::from_path(config, dir)?); | ||
23 | } | ||
24 | Ok(Self { | ||
25 | // UUID not really needed, maybe a shorter hash | ||
26 | id: Uuid::new_v4().to_string(), | ||
27 | timestamp: SystemTime::now() | ||
28 | .duration_since(UNIX_EPOCH) | ||
29 | .unwrap() | ||
30 | .as_secs(), | ||
31 | packages, | ||
32 | files, | ||
33 | }) | ||
34 | } | ||
35 | |||
36 | |||
37 | } | ||
38 | |||
39 | struct BackupLocation { | ||
40 | id: BackupId, | ||
41 | rel_location: String, | ||
42 | } | ||
43 | |||
44 | type BackupList = Vec<BackupLocation>; | ||
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..625118a --- /dev/null +++ b/src/config.rs | |||
@@ -0,0 +1,33 @@ | |||
1 | use config::{File, Map}; | ||
2 | use serde::{Deserialize, Serialize}; | ||
3 | |||
4 | #[derive(Debug, Serialize, Deserialize)] | ||
5 | #[serde(default)] | ||
6 | pub struct Config { | ||
7 | pub root: String, | ||
8 | pub user: Vec<String>, | ||
9 | pub directories: Vec<String>, | ||
10 | pub custom_directories: Map<String, String> | ||
11 | } | ||
12 | |||
13 | impl Default for Config { | ||
14 | fn default() -> Self { | ||
15 | Self { | ||
16 | root: "/mnt/backup".to_string(), | ||
17 | user: vec![], | ||
18 | directories: vec![], | ||
19 | custom_directories: Map::new(), | ||
20 | } | ||
21 | } | ||
22 | } | ||
23 | |||
24 | impl Config { | ||
25 | pub fn load() -> Result<Self, config::ConfigError> { | ||
26 | let config = config::Config::builder() | ||
27 | .add_source(File::with_name("config.toml").required(false)) | ||
28 | .add_source(config::Environment::with_prefix("FXBAUP").separator("_")) | ||
29 | .build()?; | ||
30 | |||
31 | config.try_deserialize() | ||
32 | } | ||
33 | } | ||
diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..77eab69 --- /dev/null +++ b/src/error.rs | |||
@@ -0,0 +1,19 @@ | |||
1 | pub type Result<T> = std::result::Result<T, Error>; | ||
2 | |||
3 | #[derive(Debug, PartialEq, Eq, thiserror::Error)] | ||
4 | pub enum Error { | ||
5 | #[error("unknown custom directory '{0}'")] | ||
6 | CustomDirectory(String), | ||
7 | |||
8 | #[error("invalid directory index '{0}'")] | ||
9 | InvalidIndex(String), | ||
10 | |||
11 | #[error("no directory index given")] | ||
12 | NoIndex, | ||
13 | |||
14 | #[error("invalid directory '{0}'")] | ||
15 | InvalidDirectory(String), | ||
16 | |||
17 | #[error("Only exactly one user allowed in config")] | ||
18 | MultiUser, | ||
19 | } | ||
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1fdcebf --- /dev/null +++ b/src/main.rs | |||
@@ -0,0 +1,32 @@ | |||
1 | use backup::Backup; | ||
2 | use config::Config; | ||
3 | use packages::{pacman::Pacman, PackageManager}; | ||
4 | use storage::save_index; | ||
5 | |||
6 | mod backup; | ||
7 | mod config; | ||
8 | mod error; | ||
9 | mod pathinfo; | ||
10 | mod packages; | ||
11 | mod storage; | ||
12 | |||
13 | fn main() -> anyhow::Result<()> { | ||
14 | let mut cfg = Config::load()?; | ||
15 | cfg.user.push("fx".to_string()); | ||
16 | cfg.directories.push("~/.config/nvim".to_string()); | ||
17 | cfg.directories.push("~/.config/hypr".to_string()); | ||
18 | let toml = toml::to_string(&cfg)?; | ||
19 | println!("{toml}"); | ||
20 | |||
21 | let pacman = Pacman; | ||
22 | let pkgs = pacman.get_installed(); | ||
23 | |||
24 | let backup = Backup::create(&cfg, pkgs)?; | ||
25 | // println!("{backup:#?}"); | ||
26 | |||
27 | save_index(backup); | ||
28 | |||
29 | // let fi = FileInfo::new("~/.config/nvim", &cfg)?; | ||
30 | // println!("{:?}", fi.get_absolute_path()); | ||
31 | Ok(()) | ||
32 | } | ||
diff --git a/src/packages.rs b/src/packages.rs new file mode 100644 index 0000000..9f765d6 --- /dev/null +++ b/src/packages.rs | |||
@@ -0,0 +1,16 @@ | |||
1 | use serde::{Deserialize, Serialize}; | ||
2 | |||
3 | pub mod pacman; | ||
4 | |||
5 | #[derive(Debug, Serialize, Deserialize)] | ||
6 | pub struct Package { | ||
7 | pub id: String, | ||
8 | pub version: String, | ||
9 | pub explicit: bool | ||
10 | } | ||
11 | |||
12 | pub trait PackageManager { | ||
13 | fn get_installed(&self) -> Vec<Package>; | ||
14 | |||
15 | fn install(&self, pkgs: Vec<Package>); | ||
16 | } | ||
diff --git a/src/packages/pacman.rs b/src/packages/pacman.rs new file mode 100644 index 0000000..0a9e1ff --- /dev/null +++ b/src/packages/pacman.rs | |||
@@ -0,0 +1,46 @@ | |||
1 | use std::process::Command; | ||
2 | |||
3 | use crate::packages::Package; | ||
4 | |||
5 | use super::PackageManager; | ||
6 | |||
7 | pub struct Pacman; | ||
8 | |||
9 | impl PackageManager for Pacman { | ||
10 | fn get_installed(&self) -> Vec<super::Package> { | ||
11 | let pm_pkgs = Command::new("pacman").args(["-Q"]).output().unwrap(); | ||
12 | let pm_e_pkgs = Command::new("pacman") | ||
13 | .args(["-Q", "--explicit"]) | ||
14 | .output() | ||
15 | .unwrap(); | ||
16 | |||
17 | let pm_pkgs_out = String::from_utf8(pm_pkgs.stdout).unwrap(); | ||
18 | let pm_e_pkgs_out = String::from_utf8(pm_e_pkgs.stdout).unwrap(); | ||
19 | |||
20 | let mut pkgs: Vec<Package> = Vec::new(); | ||
21 | let pacman_pkgs: Vec<&str> = pm_pkgs_out.split('\n').collect(); | ||
22 | for pkg in pacman_pkgs { | ||
23 | if pkg.is_empty() { | ||
24 | continue; | ||
25 | }; | ||
26 | let split: Vec<&str> = pkg.split_whitespace().collect(); | ||
27 | if split.len() != 2 { | ||
28 | panic!("Unknown Pacman Output"); | ||
29 | }; | ||
30 | |||
31 | let explicit = pm_e_pkgs_out.contains(pkg); | ||
32 | |||
33 | pkgs.push(Package { | ||
34 | id: split[0].to_string(), | ||
35 | version: split[1].to_string(), | ||
36 | explicit | ||
37 | }) | ||
38 | } | ||
39 | |||
40 | pkgs | ||
41 | } | ||
42 | |||
43 | fn install(&self, pkgs: Vec<Package>) { | ||
44 | todo!(); | ||
45 | } | ||
46 | } | ||
diff --git a/src/pathinfo.rs b/src/pathinfo.rs new file mode 100644 index 0000000..b0c3be4 --- /dev/null +++ b/src/pathinfo.rs | |||
@@ -0,0 +1,246 @@ | |||
1 | use std::{ | ||
2 | fmt::Display, | ||
3 | path::PathBuf, | ||
4 | }; | ||
5 | |||
6 | use serde::{Deserialize, Serialize}; | ||
7 | |||
8 | use crate::{ | ||
9 | backup::BackupId, | ||
10 | config::Config, | ||
11 | error::{Error, Result}, | ||
12 | }; | ||
13 | |||
14 | #[derive(Debug, Clone, Serialize, Deserialize)] | ||
15 | pub struct PathInfo { | ||
16 | pub modified: bool, | ||
17 | pub is_file: bool, | ||
18 | rel_location: String, | ||
19 | location_root: LocationRoot, | ||
20 | last_modified: BackupId, | ||
21 | children: Vec<PathInfo> | ||
22 | } | ||
23 | |||
24 | impl PathInfo { | ||
25 | pub fn from_path(config: &Config, path: &str) -> Result<Self> { | ||
26 | let locations = Self::parse_location(path, config)?; | ||
27 | |||
28 | Ok(Self::handle_dir(config, &locations.0, &locations.1)?) | ||
29 | } | ||
30 | |||
31 | fn handle_dir( | ||
32 | config: &Config, | ||
33 | rel_location: &str, | ||
34 | location_root: &LocationRoot, | ||
35 | ) -> Result<Self> { | ||
36 | println!("Handling {rel_location}"); | ||
37 | let path = Self::get_abs_path(&location_root.to_string(), rel_location); | ||
38 | Ok(if path.is_dir() { | ||
39 | let mut modified = false; | ||
40 | let mut children: Vec<PathInfo> = Vec::new(); | ||
41 | |||
42 | let paths = std::fs::read_dir(path).unwrap(); | ||
43 | for path in paths { | ||
44 | let pathstr = path.unwrap().path().to_string_lossy().to_string(); | ||
45 | let root = format!("{}/", location_root.to_string()); | ||
46 | let Some(rl) = pathstr.split_once(&root) else { | ||
47 | panic!("HUH"); | ||
48 | }; | ||
49 | let handle = Self::handle_dir(config, rl.1, location_root)?; | ||
50 | if handle.modified { | ||
51 | modified = true; | ||
52 | }; | ||
53 | children.push(handle); | ||
54 | } | ||
55 | Self { | ||
56 | modified, | ||
57 | is_file: false, | ||
58 | rel_location: rel_location.to_string(), | ||
59 | location_root: location_root.clone(), | ||
60 | last_modified: "".to_string(), | ||
61 | children | ||
62 | } | ||
63 | } else { | ||
64 | Self::from_file(rel_location, location_root.clone())? | ||
65 | }) | ||
66 | } | ||
67 | |||
68 | fn from_file(rel_location: &str, location_root: LocationRoot) -> Result<Self> { | ||
69 | println!("From file {rel_location}"); | ||
70 | |||
71 | let modified = false; | ||
72 | |||
73 | Ok(Self { | ||
74 | rel_location: rel_location.to_string(), | ||
75 | location_root, | ||
76 | modified, | ||
77 | last_modified: "".to_string(), | ||
78 | is_file: true, | ||
79 | children: Vec::new() | ||
80 | }) | ||
81 | } | ||
82 | |||
83 | pub fn get_absolute_path(&self) -> PathBuf { | ||
84 | Self::get_abs_path(&self.location_root.to_string(), &self.rel_location) | ||
85 | } | ||
86 | |||
87 | fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf { | ||
88 | let path = format!("{}/{}", location_root, rel_location); | ||
89 | PathBuf::from(path) | ||
90 | } | ||
91 | |||
92 | fn parse_location(value: &str, config: &Config) -> Result<(String, LocationRoot)> { | ||
93 | let Some(split) = value.split_once('/') else { | ||
94 | return Err(Error::InvalidDirectory(value.to_string())); | ||
95 | }; | ||
96 | if split.0.starts_with('~') { | ||
97 | if config.user.len() != 1 { | ||
98 | return Err(Error::MultiUser); | ||
99 | } | ||
100 | return Ok(( | ||
101 | split.1.to_string(), | ||
102 | LocationRoot::User(config.user[0].clone()), | ||
103 | )); | ||
104 | }; | ||
105 | Ok(( | ||
106 | split.1.to_string(), | ||
107 | LocationRoot::from_op_str(split.0, config)?, | ||
108 | )) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
113 | pub enum LocationRoot { | ||
114 | User(String), | ||
115 | Custom(String), | ||
116 | SystemSettings, | ||
117 | Root, | ||
118 | } | ||
119 | |||
120 | impl Display for LocationRoot { | ||
121 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
122 | match self { | ||
123 | LocationRoot::User(user) => write!(f, "/home/{user}"), | ||
124 | LocationRoot::Custom(loc) => write!(f, "{loc}"), | ||
125 | LocationRoot::SystemSettings => write!(f, "/etc"), | ||
126 | LocationRoot::Root => write!(f, "/"), | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | impl LocationRoot { | ||
132 | fn from_op_str(value: &str, config: &Config) -> Result<Self> { | ||
133 | let split_str = value.split_once(':'); | ||
134 | let Some(split_op) = split_str else { | ||
135 | return Err(Error::NoIndex); | ||
136 | }; | ||
137 | match split_op.0 { | ||
138 | "u" => Ok(Self::User(split_op.1.to_string())), | ||
139 | "s" => Ok(Self::SystemSettings), | ||
140 | "r" => Ok(Self::Root), | ||
141 | "c" => Ok(Self::Custom( | ||
142 | config | ||
143 | .custom_directories | ||
144 | .get(split_op.1) | ||
145 | .ok_or_else(|| Error::CustomDirectory(split_op.1.to_string()))? | ||
146 | .to_string(), | ||
147 | )), | ||
148 | _ => Err(Error::InvalidIndex(split_op.0.to_string())), | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | #[cfg(test)] | ||
154 | mod tests { | ||
155 | use crate::{ | ||
156 | config::Config, | ||
157 | error::{Error, Result}, | ||
158 | pathinfo::PathInfo, | ||
159 | }; | ||
160 | |||
161 | use super::LocationRoot; | ||
162 | |||
163 | #[test] | ||
164 | fn from_op_str() -> Result<()> { | ||
165 | let mut config = Config::default(); | ||
166 | config | ||
167 | .custom_directories | ||
168 | .insert("test".to_string(), "/usr/local/test".to_string()); | ||
169 | |||
170 | let mut values: Vec<(&str, Result<LocationRoot>)> = Vec::new(); | ||
171 | values.push(("u:test", Ok(LocationRoot::User("test".to_string())))); | ||
172 | values.push(("s:", Ok(LocationRoot::SystemSettings))); | ||
173 | values.push(("r:", Ok(LocationRoot::Root))); | ||
174 | values.push(( | ||
175 | "c:test", | ||
176 | Ok(LocationRoot::Custom("/usr/local/test".to_string())), | ||
177 | )); | ||
178 | values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string())))); | ||
179 | values.push(("t:test/", Err(Error::InvalidIndex("t".to_string())))); | ||
180 | values.push(( | ||
181 | "test:test/usr", | ||
182 | Err(Error::InvalidIndex("test".to_string())), | ||
183 | )); | ||
184 | values.push(("/usr/local/test", Err(Error::NoIndex))); | ||
185 | values.push(("c/usr/local/test", Err(Error::NoIndex))); | ||
186 | |||
187 | for value in values { | ||
188 | print!("Testing {value:?}"); | ||
189 | assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1); | ||
190 | println!("\rTesting {value:?} ✓"); | ||
191 | } | ||
192 | |||
193 | Ok(()) | ||
194 | } | ||
195 | |||
196 | #[test] | ||
197 | fn parse_location() -> Result<()> { | ||
198 | let mut config = Config::default(); | ||
199 | config.user.push("test".to_string()); | ||
200 | config | ||
201 | .custom_directories | ||
202 | .insert("test".to_string(), "/usr/local/test".to_string()); | ||
203 | |||
204 | let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new(); | ||
205 | values.push(( | ||
206 | "~/.config/nvim", | ||
207 | Ok(( | ||
208 | ".config/nvim".to_string(), | ||
209 | LocationRoot::User("test".to_string()), | ||
210 | )), | ||
211 | )); | ||
212 | values.push(( | ||
213 | "u:test/.config/nvim", | ||
214 | Ok(( | ||
215 | ".config/nvim".to_string(), | ||
216 | LocationRoot::User("test".to_string()), | ||
217 | )), | ||
218 | )); | ||
219 | values.push(( | ||
220 | "r:/.config/nvim", | ||
221 | Ok((".config/nvim".to_string(), LocationRoot::Root)), | ||
222 | )); | ||
223 | values.push(( | ||
224 | "r:/.config/nvim", | ||
225 | Ok((".config/nvim".to_string(), LocationRoot::Root)), | ||
226 | )); | ||
227 | values.push(( | ||
228 | "s:/.config/nvim", | ||
229 | Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)), | ||
230 | )); | ||
231 | values.push(( | ||
232 | "c:test/.config/nvim", | ||
233 | Ok(( | ||
234 | ".config/nvim".to_string(), | ||
235 | LocationRoot::Custom("/usr/local/test".to_string()), | ||
236 | )), | ||
237 | )); | ||
238 | |||
239 | for value in values { | ||
240 | print!("Testing {value:?}"); | ||
241 | assert_eq!(PathInfo::parse_location(&value.0, &config), value.1); | ||
242 | println!("\rTesting {value:?} ✓"); | ||
243 | } | ||
244 | Ok(()) | ||
245 | } | ||
246 | } | ||
diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 0000000..b9e8de9 --- /dev/null +++ b/src/storage.rs | |||
@@ -0,0 +1,8 @@ | |||
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 | } | ||