summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock564
-rw-r--r--Cargo.toml13
-rw-r--r--src/backup.rs44
-rw-r--r--src/config.rs33
-rw-r--r--src/error.rs19
-rw-r--r--src/main.rs32
-rw-r--r--src/packages.rs16
-rw-r--r--src/packages/pacman.rs46
-rw-r--r--src/pathinfo.rs246
-rw-r--r--src/storage.rs8
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.
3version = 3
4
5[[package]]
6name = "anyhow"
7version = "1.0.86"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
10
11[[package]]
12name = "async-trait"
13version = "0.1.82"
14source = "registry+https://github.com/rust-lang/crates.io-index"
15checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
16dependencies = [
17 "proc-macro2",
18 "quote",
19 "syn",
20]
21
22[[package]]
23name = "base64"
24version = "0.21.7"
25source = "registry+https://github.com/rust-lang/crates.io-index"
26checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
27
28[[package]]
29name = "bitflags"
30version = "2.6.0"
31source = "registry+https://github.com/rust-lang/crates.io-index"
32checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
33dependencies = [
34 "serde",
35]
36
37[[package]]
38name = "block-buffer"
39version = "0.10.4"
40source = "registry+https://github.com/rust-lang/crates.io-index"
41checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
42dependencies = [
43 "generic-array",
44]
45
46[[package]]
47name = "cfg-if"
48version = "1.0.0"
49source = "registry+https://github.com/rust-lang/crates.io-index"
50checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
51
52[[package]]
53name = "config"
54version = "0.14.0"
55source = "registry+https://github.com/rust-lang/crates.io-index"
56checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
57dependencies = [
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]]
73name = "const-random"
74version = "0.1.18"
75source = "registry+https://github.com/rust-lang/crates.io-index"
76checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
77dependencies = [
78 "const-random-macro",
79]
80
81[[package]]
82name = "const-random-macro"
83version = "0.1.16"
84source = "registry+https://github.com/rust-lang/crates.io-index"
85checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
86dependencies = [
87 "getrandom",
88 "once_cell",
89 "tiny-keccak",
90]
91
92[[package]]
93name = "convert_case"
94version = "0.6.0"
95source = "registry+https://github.com/rust-lang/crates.io-index"
96checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
97dependencies = [
98 "unicode-segmentation",
99]
100
101[[package]]
102name = "cpufeatures"
103version = "0.2.13"
104source = "registry+https://github.com/rust-lang/crates.io-index"
105checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
106dependencies = [
107 "libc",
108]
109
110[[package]]
111name = "crunchy"
112version = "0.2.2"
113source = "registry+https://github.com/rust-lang/crates.io-index"
114checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
115
116[[package]]
117name = "crypto-common"
118version = "0.1.6"
119source = "registry+https://github.com/rust-lang/crates.io-index"
120checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
121dependencies = [
122 "generic-array",
123 "typenum",
124]
125
126[[package]]
127name = "digest"
128version = "0.10.7"
129source = "registry+https://github.com/rust-lang/crates.io-index"
130checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
131dependencies = [
132 "block-buffer",
133 "crypto-common",
134]
135
136[[package]]
137name = "dlv-list"
138version = "0.5.2"
139source = "registry+https://github.com/rust-lang/crates.io-index"
140checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
141dependencies = [
142 "const-random",
143]
144
145[[package]]
146name = "equivalent"
147version = "1.0.1"
148source = "registry+https://github.com/rust-lang/crates.io-index"
149checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
150
151[[package]]
152name = "fxbaup"
153version = "0.1.0"
154dependencies = [
155 "anyhow",
156 "config",
157 "serde",
158 "serde_json",
159 "thiserror",
160 "toml",
161 "uuid",
162]
163
164[[package]]
165name = "generic-array"
166version = "0.14.7"
167source = "registry+https://github.com/rust-lang/crates.io-index"
168checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
169dependencies = [
170 "typenum",
171 "version_check",
172]
173
174[[package]]
175name = "getrandom"
176version = "0.2.15"
177source = "registry+https://github.com/rust-lang/crates.io-index"
178checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
179dependencies = [
180 "cfg-if",
181 "libc",
182 "wasi",
183]
184
185[[package]]
186name = "hashbrown"
187version = "0.13.2"
188source = "registry+https://github.com/rust-lang/crates.io-index"
189checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
190
191[[package]]
192name = "hashbrown"
193version = "0.14.5"
194source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
196
197[[package]]
198name = "indexmap"
199version = "2.5.0"
200source = "registry+https://github.com/rust-lang/crates.io-index"
201checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
202dependencies = [
203 "equivalent",
204 "hashbrown 0.14.5",
205]
206
207[[package]]
208name = "itoa"
209version = "1.0.11"
210source = "registry+https://github.com/rust-lang/crates.io-index"
211checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
212
213[[package]]
214name = "json5"
215version = "0.4.1"
216source = "registry+https://github.com/rust-lang/crates.io-index"
217checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
218dependencies = [
219 "pest",
220 "pest_derive",
221 "serde",
222]
223
224[[package]]
225name = "lazy_static"
226version = "1.5.0"
227source = "registry+https://github.com/rust-lang/crates.io-index"
228checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
229
230[[package]]
231name = "libc"
232version = "0.2.158"
233source = "registry+https://github.com/rust-lang/crates.io-index"
234checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
235
236[[package]]
237name = "linked-hash-map"
238version = "0.5.6"
239source = "registry+https://github.com/rust-lang/crates.io-index"
240checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
241
242[[package]]
243name = "memchr"
244version = "2.7.4"
245source = "registry+https://github.com/rust-lang/crates.io-index"
246checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
247
248[[package]]
249name = "minimal-lexical"
250version = "0.2.1"
251source = "registry+https://github.com/rust-lang/crates.io-index"
252checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
253
254[[package]]
255name = "nom"
256version = "7.1.3"
257source = "registry+https://github.com/rust-lang/crates.io-index"
258checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
259dependencies = [
260 "memchr",
261 "minimal-lexical",
262]
263
264[[package]]
265name = "once_cell"
266version = "1.19.0"
267source = "registry+https://github.com/rust-lang/crates.io-index"
268checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
269
270[[package]]
271name = "ordered-multimap"
272version = "0.6.0"
273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
275dependencies = [
276 "dlv-list",
277 "hashbrown 0.13.2",
278]
279
280[[package]]
281name = "pathdiff"
282version = "0.2.1"
283source = "registry+https://github.com/rust-lang/crates.io-index"
284checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
285
286[[package]]
287name = "pest"
288version = "2.7.11"
289source = "registry+https://github.com/rust-lang/crates.io-index"
290checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
291dependencies = [
292 "memchr",
293 "thiserror",
294 "ucd-trie",
295]
296
297[[package]]
298name = "pest_derive"
299version = "2.7.11"
300source = "registry+https://github.com/rust-lang/crates.io-index"
301checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
302dependencies = [
303 "pest",
304 "pest_generator",
305]
306
307[[package]]
308name = "pest_generator"
309version = "2.7.11"
310source = "registry+https://github.com/rust-lang/crates.io-index"
311checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
312dependencies = [
313 "pest",
314 "pest_meta",
315 "proc-macro2",
316 "quote",
317 "syn",
318]
319
320[[package]]
321name = "pest_meta"
322version = "2.7.11"
323source = "registry+https://github.com/rust-lang/crates.io-index"
324checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
325dependencies = [
326 "once_cell",
327 "pest",
328 "sha2",
329]
330
331[[package]]
332name = "proc-macro2"
333version = "1.0.86"
334source = "registry+https://github.com/rust-lang/crates.io-index"
335checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
336dependencies = [
337 "unicode-ident",
338]
339
340[[package]]
341name = "quote"
342version = "1.0.37"
343source = "registry+https://github.com/rust-lang/crates.io-index"
344checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
345dependencies = [
346 "proc-macro2",
347]
348
349[[package]]
350name = "ron"
351version = "0.8.1"
352source = "registry+https://github.com/rust-lang/crates.io-index"
353checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
354dependencies = [
355 "base64",
356 "bitflags",
357 "serde",
358 "serde_derive",
359]
360
361[[package]]
362name = "rust-ini"
363version = "0.19.0"
364source = "registry+https://github.com/rust-lang/crates.io-index"
365checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
366dependencies = [
367 "cfg-if",
368 "ordered-multimap",
369]
370
371[[package]]
372name = "ryu"
373version = "1.0.18"
374source = "registry+https://github.com/rust-lang/crates.io-index"
375checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
376
377[[package]]
378name = "serde"
379version = "1.0.209"
380source = "registry+https://github.com/rust-lang/crates.io-index"
381checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
382dependencies = [
383 "serde_derive",
384]
385
386[[package]]
387name = "serde_derive"
388version = "1.0.209"
389source = "registry+https://github.com/rust-lang/crates.io-index"
390checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
391dependencies = [
392 "proc-macro2",
393 "quote",
394 "syn",
395]
396
397[[package]]
398name = "serde_json"
399version = "1.0.128"
400source = "registry+https://github.com/rust-lang/crates.io-index"
401checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
402dependencies = [
403 "itoa",
404 "memchr",
405 "ryu",
406 "serde",
407]
408
409[[package]]
410name = "serde_spanned"
411version = "0.6.7"
412source = "registry+https://github.com/rust-lang/crates.io-index"
413checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
414dependencies = [
415 "serde",
416]
417
418[[package]]
419name = "sha2"
420version = "0.10.8"
421source = "registry+https://github.com/rust-lang/crates.io-index"
422checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
423dependencies = [
424 "cfg-if",
425 "cpufeatures",
426 "digest",
427]
428
429[[package]]
430name = "syn"
431version = "2.0.77"
432source = "registry+https://github.com/rust-lang/crates.io-index"
433checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
434dependencies = [
435 "proc-macro2",
436 "quote",
437 "unicode-ident",
438]
439
440[[package]]
441name = "thiserror"
442version = "1.0.63"
443source = "registry+https://github.com/rust-lang/crates.io-index"
444checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
445dependencies = [
446 "thiserror-impl",
447]
448
449[[package]]
450name = "thiserror-impl"
451version = "1.0.63"
452source = "registry+https://github.com/rust-lang/crates.io-index"
453checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
454dependencies = [
455 "proc-macro2",
456 "quote",
457 "syn",
458]
459
460[[package]]
461name = "tiny-keccak"
462version = "2.0.2"
463source = "registry+https://github.com/rust-lang/crates.io-index"
464checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
465dependencies = [
466 "crunchy",
467]
468
469[[package]]
470name = "toml"
471version = "0.8.19"
472source = "registry+https://github.com/rust-lang/crates.io-index"
473checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
474dependencies = [
475 "serde",
476 "serde_spanned",
477 "toml_datetime",
478 "toml_edit",
479]
480
481[[package]]
482name = "toml_datetime"
483version = "0.6.8"
484source = "registry+https://github.com/rust-lang/crates.io-index"
485checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
486dependencies = [
487 "serde",
488]
489
490[[package]]
491name = "toml_edit"
492version = "0.22.20"
493source = "registry+https://github.com/rust-lang/crates.io-index"
494checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
495dependencies = [
496 "indexmap",
497 "serde",
498 "serde_spanned",
499 "toml_datetime",
500 "winnow",
501]
502
503[[package]]
504name = "typenum"
505version = "1.17.0"
506source = "registry+https://github.com/rust-lang/crates.io-index"
507checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
508
509[[package]]
510name = "ucd-trie"
511version = "0.1.6"
512source = "registry+https://github.com/rust-lang/crates.io-index"
513checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
514
515[[package]]
516name = "unicode-ident"
517version = "1.0.12"
518source = "registry+https://github.com/rust-lang/crates.io-index"
519checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
520
521[[package]]
522name = "unicode-segmentation"
523version = "1.11.0"
524source = "registry+https://github.com/rust-lang/crates.io-index"
525checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
526
527[[package]]
528name = "uuid"
529version = "1.10.0"
530source = "registry+https://github.com/rust-lang/crates.io-index"
531checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
532dependencies = [
533 "getrandom",
534]
535
536[[package]]
537name = "version_check"
538version = "0.9.5"
539source = "registry+https://github.com/rust-lang/crates.io-index"
540checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
541
542[[package]]
543name = "wasi"
544version = "0.11.0+wasi-snapshot-preview1"
545source = "registry+https://github.com/rust-lang/crates.io-index"
546checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
547
548[[package]]
549name = "winnow"
550version = "0.6.18"
551source = "registry+https://github.com/rust-lang/crates.io-index"
552checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
553dependencies = [
554 "memchr",
555]
556
557[[package]]
558name = "yaml-rust"
559version = "0.4.5"
560source = "registry+https://github.com/rust-lang/crates.io-index"
561checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
562dependencies = [
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]
2name = "fxbaup"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7anyhow = "1.0.86"
8config = "0.14.0"
9serde = { version = "1.0.209", features = ["derive"] }
10serde_json = "1.0.128"
11thiserror = "1.0.63"
12toml = "0.8.19"
13uuid = { 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 @@
1use std::time::{SystemTime, UNIX_EPOCH};
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::{config::Config, pathinfo::PathInfo, packages::Package, error::Result};
7
8pub type BackupId = String;
9
10#[derive(Debug, Serialize, Deserialize)]
11pub struct Backup {
12 id: String,
13 timestamp: u64,
14 packages: Vec<Package>,
15 files: Vec<PathInfo>,
16}
17
18impl 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
39struct BackupLocation {
40 id: BackupId,
41 rel_location: String,
42}
43
44type 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 @@
1use config::{File, Map};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Serialize, Deserialize)]
5#[serde(default)]
6pub 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
13impl 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
24impl 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 @@
1pub type Result<T> = std::result::Result<T, Error>;
2
3#[derive(Debug, PartialEq, Eq, thiserror::Error)]
4pub 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 @@
1use backup::Backup;
2use config::Config;
3use packages::{pacman::Pacman, PackageManager};
4use storage::save_index;
5
6mod backup;
7mod config;
8mod error;
9mod pathinfo;
10mod packages;
11mod storage;
12
13fn 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 @@
1use serde::{Deserialize, Serialize};
2
3pub mod pacman;
4
5#[derive(Debug, Serialize, Deserialize)]
6pub struct Package {
7 pub id: String,
8 pub version: String,
9 pub explicit: bool
10}
11
12pub 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 @@
1use std::process::Command;
2
3use crate::packages::Package;
4
5use super::PackageManager;
6
7pub struct Pacman;
8
9impl 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 @@
1use std::{
2 fmt::Display,
3 path::PathBuf,
4};
5
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 backup::BackupId,
10 config::Config,
11 error::{Error, Result},
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub 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
24impl 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)]
113pub enum LocationRoot {
114 User(String),
115 Custom(String),
116 SystemSettings,
117 Root,
118}
119
120impl 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
131impl 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)]
154mod 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 @@
1use std::{fs::File, io::Write};
2
3use crate::backup::Backup;
4
5pub 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}