aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2022-02-08 09:21:12 +0000
committerdiogo464 <[email protected]>2022-02-08 09:21:12 +0000
commit92b05a877eb772985d2f4fc9cd198ca642b69b6a (patch)
tree4a2edc173b8809e929e4abc47899ae9c6d956684
parent0992c36733f58750da93921041424fd09f0158ed (diff)
removed old code
-rw-r--r--Cargo.lock627
-rw-r--r--Cargo.toml2
-rw-r--r--dotup/Cargo.toml14
-rw-r--r--dotup/src/archive.rs44
-rw-r--r--dotup/src/depot.rs484
-rw-r--r--dotup/src/error.rs21
-rw-r--r--dotup/src/lib.rs18
-rw-r--r--dotup/src/utils.rs94
-rw-r--r--dotup/tests/integration_tests.rs126
-rw-r--r--dotup/tests/testing_depot.toml7
-rw-r--r--dotup_cli/Cargo.toml29
-rw-r--r--dotup_cli/src/commands/init.rs22
-rw-r--r--dotup_cli/src/commands/install.rs25
-rw-r--r--dotup_cli/src/commands/link.rs134
-rw-r--r--dotup_cli/src/commands/mod.rs11
-rw-r--r--dotup_cli/src/commands/mv.rs53
-rw-r--r--dotup_cli/src/commands/status.rs175
-rw-r--r--dotup_cli/src/commands/uninstall.rs26
-rw-r--r--dotup_cli/src/commands/unlink.rs43
-rw-r--r--dotup_cli/src/config.rs10
-rw-r--r--dotup_cli/src/main.rs111
-rw-r--r--dotup_cli/src/utils.rs182
-rw-r--r--dotup_cli/tests/cli.rs145
23 files changed, 0 insertions, 2403 deletions
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index dd07c28..0000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,627 +0,0 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 3
4
5[[package]]
6name = "aho-corasick"
7version = "0.7.18"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
10dependencies = [
11 "memchr",
12]
13
14[[package]]
15name = "ansi_term"
16version = "0.12.1"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
19dependencies = [
20 "winapi",
21]
22
23[[package]]
24name = "anyhow"
25version = "1.0.51"
26source = "registry+https://github.com/rust-lang/crates.io-index"
27checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
28
29[[package]]
30name = "assert_cmd"
31version = "2.0.2"
32source = "registry+https://github.com/rust-lang/crates.io-index"
33checksum = "e996dc7940838b7ef1096b882e29ec30a3149a3a443cdc8dba19ed382eca1fe2"
34dependencies = [
35 "bstr",
36 "doc-comment",
37 "predicates",
38 "predicates-core",
39 "predicates-tree",
40 "wait-timeout",
41]
42
43[[package]]
44name = "atty"
45version = "0.2.14"
46source = "registry+https://github.com/rust-lang/crates.io-index"
47checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
48dependencies = [
49 "hermit-abi",
50 "libc",
51 "winapi",
52]
53
54[[package]]
55name = "autocfg"
56version = "1.0.1"
57source = "registry+https://github.com/rust-lang/crates.io-index"
58checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
59
60[[package]]
61name = "bitflags"
62version = "1.2.1"
63source = "registry+https://github.com/rust-lang/crates.io-index"
64checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
65
66[[package]]
67name = "bstr"
68version = "0.2.16"
69source = "registry+https://github.com/rust-lang/crates.io-index"
70checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
71dependencies = [
72 "lazy_static",
73 "memchr",
74 "regex-automata",
75]
76
77[[package]]
78name = "cfg-if"
79version = "1.0.0"
80source = "registry+https://github.com/rust-lang/crates.io-index"
81checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
82
83[[package]]
84name = "clap"
85version = "3.0.0-rc.7"
86source = "registry+https://github.com/rust-lang/crates.io-index"
87checksum = "9468f8012246b0836c6fd11725102b0844254985f2462b6c637d50040ef49df0"
88dependencies = [
89 "atty",
90 "bitflags",
91 "clap_derive",
92 "indexmap",
93 "lazy_static",
94 "os_str_bytes",
95 "strsim",
96 "termcolor",
97 "textwrap",
98]
99
100[[package]]
101name = "clap_derive"
102version = "3.0.0-rc.7"
103source = "registry+https://github.com/rust-lang/crates.io-index"
104checksum = "b72e1af32a4de4d21a43d26de33fe69c00e895371bd8b1523d674f011b610467"
105dependencies = [
106 "heck",
107 "proc-macro-error",
108 "proc-macro2",
109 "quote",
110 "syn",
111]
112
113[[package]]
114name = "difflib"
115version = "0.4.0"
116source = "registry+https://github.com/rust-lang/crates.io-index"
117checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
118
119[[package]]
120name = "doc-comment"
121version = "0.3.3"
122source = "registry+https://github.com/rust-lang/crates.io-index"
123checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
124
125[[package]]
126name = "dotup"
127version = "0.1.0"
128dependencies = [
129 "log",
130 "serde",
131 "slotmap",
132 "tempfile",
133 "thiserror",
134 "toml",
135]
136
137[[package]]
138name = "dotup_cli"
139version = "0.1.0"
140dependencies = [
141 "ansi_term",
142 "anyhow",
143 "assert_cmd",
144 "clap",
145 "dotup",
146 "flexi_logger",
147 "log",
148 "tempfile",
149]
150
151[[package]]
152name = "either"
153version = "1.6.1"
154source = "registry+https://github.com/rust-lang/crates.io-index"
155checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
156
157[[package]]
158name = "flexi_logger"
159version = "0.22.0"
160source = "registry+https://github.com/rust-lang/crates.io-index"
161checksum = "11be38a063886b7be57de89636d65c07d318c1f9bd985cd8ab2c343786a910bc"
162dependencies = [
163 "ansi_term",
164 "atty",
165 "glob",
166 "lazy_static",
167 "log",
168 "regex",
169 "rustversion",
170 "thiserror",
171 "time",
172]
173
174[[package]]
175name = "getrandom"
176version = "0.2.3"
177source = "registry+https://github.com/rust-lang/crates.io-index"
178checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
179dependencies = [
180 "cfg-if",
181 "libc",
182 "wasi",
183]
184
185[[package]]
186name = "glob"
187version = "0.3.0"
188source = "registry+https://github.com/rust-lang/crates.io-index"
189checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
190
191[[package]]
192name = "hashbrown"
193version = "0.11.2"
194source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
196
197[[package]]
198name = "heck"
199version = "0.3.3"
200source = "registry+https://github.com/rust-lang/crates.io-index"
201checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
202dependencies = [
203 "unicode-segmentation",
204]
205
206[[package]]
207name = "hermit-abi"
208version = "0.1.19"
209source = "registry+https://github.com/rust-lang/crates.io-index"
210checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
211dependencies = [
212 "libc",
213]
214
215[[package]]
216name = "indexmap"
217version = "1.7.0"
218source = "registry+https://github.com/rust-lang/crates.io-index"
219checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
220dependencies = [
221 "autocfg",
222 "hashbrown",
223]
224
225[[package]]
226name = "itertools"
227version = "0.10.1"
228source = "registry+https://github.com/rust-lang/crates.io-index"
229checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
230dependencies = [
231 "either",
232]
233
234[[package]]
235name = "itoa"
236version = "0.4.8"
237source = "registry+https://github.com/rust-lang/crates.io-index"
238checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
239
240[[package]]
241name = "lazy_static"
242version = "1.4.0"
243source = "registry+https://github.com/rust-lang/crates.io-index"
244checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
245
246[[package]]
247name = "libc"
248version = "0.2.106"
249source = "registry+https://github.com/rust-lang/crates.io-index"
250checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
251
252[[package]]
253name = "log"
254version = "0.4.14"
255source = "registry+https://github.com/rust-lang/crates.io-index"
256checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
257dependencies = [
258 "cfg-if",
259]
260
261[[package]]
262name = "memchr"
263version = "2.4.0"
264source = "registry+https://github.com/rust-lang/crates.io-index"
265checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
266
267[[package]]
268name = "os_str_bytes"
269version = "6.0.0"
270source = "registry+https://github.com/rust-lang/crates.io-index"
271checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
272dependencies = [
273 "memchr",
274]
275
276[[package]]
277name = "ppv-lite86"
278version = "0.2.10"
279source = "registry+https://github.com/rust-lang/crates.io-index"
280checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
281
282[[package]]
283name = "predicates"
284version = "2.0.0"
285source = "registry+https://github.com/rust-lang/crates.io-index"
286checksum = "c6e46ca79eb4e21e2ec14430340c71250ab69332abf85521c95d3a8bc336aa76"
287dependencies = [
288 "difflib",
289 "itertools",
290 "predicates-core",
291]
292
293[[package]]
294name = "predicates-core"
295version = "1.0.2"
296source = "registry+https://github.com/rust-lang/crates.io-index"
297checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
298
299[[package]]
300name = "predicates-tree"
301version = "1.0.2"
302source = "registry+https://github.com/rust-lang/crates.io-index"
303checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
304dependencies = [
305 "predicates-core",
306 "treeline",
307]
308
309[[package]]
310name = "proc-macro-error"
311version = "1.0.4"
312source = "registry+https://github.com/rust-lang/crates.io-index"
313checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
314dependencies = [
315 "proc-macro-error-attr",
316 "proc-macro2",
317 "quote",
318 "syn",
319 "version_check",
320]
321
322[[package]]
323name = "proc-macro-error-attr"
324version = "1.0.4"
325source = "registry+https://github.com/rust-lang/crates.io-index"
326checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
327dependencies = [
328 "proc-macro2",
329 "quote",
330 "version_check",
331]
332
333[[package]]
334name = "proc-macro2"
335version = "1.0.32"
336source = "registry+https://github.com/rust-lang/crates.io-index"
337checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
338dependencies = [
339 "unicode-xid",
340]
341
342[[package]]
343name = "quote"
344version = "1.0.9"
345source = "registry+https://github.com/rust-lang/crates.io-index"
346checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
347dependencies = [
348 "proc-macro2",
349]
350
351[[package]]
352name = "rand"
353version = "0.8.4"
354source = "registry+https://github.com/rust-lang/crates.io-index"
355checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
356dependencies = [
357 "libc",
358 "rand_chacha",
359 "rand_core",
360 "rand_hc",
361]
362
363[[package]]
364name = "rand_chacha"
365version = "0.3.1"
366source = "registry+https://github.com/rust-lang/crates.io-index"
367checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
368dependencies = [
369 "ppv-lite86",
370 "rand_core",
371]
372
373[[package]]
374name = "rand_core"
375version = "0.6.3"
376source = "registry+https://github.com/rust-lang/crates.io-index"
377checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
378dependencies = [
379 "getrandom",
380]
381
382[[package]]
383name = "rand_hc"
384version = "0.3.1"
385source = "registry+https://github.com/rust-lang/crates.io-index"
386checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
387dependencies = [
388 "rand_core",
389]
390
391[[package]]
392name = "redox_syscall"
393version = "0.2.9"
394source = "registry+https://github.com/rust-lang/crates.io-index"
395checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
396dependencies = [
397 "bitflags",
398]
399
400[[package]]
401name = "regex"
402version = "1.5.4"
403source = "registry+https://github.com/rust-lang/crates.io-index"
404checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
405dependencies = [
406 "aho-corasick",
407 "memchr",
408 "regex-syntax",
409]
410
411[[package]]
412name = "regex-automata"
413version = "0.1.10"
414source = "registry+https://github.com/rust-lang/crates.io-index"
415checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
416
417[[package]]
418name = "regex-syntax"
419version = "0.6.25"
420source = "registry+https://github.com/rust-lang/crates.io-index"
421checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
422
423[[package]]
424name = "remove_dir_all"
425version = "0.5.3"
426source = "registry+https://github.com/rust-lang/crates.io-index"
427checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
428dependencies = [
429 "winapi",
430]
431
432[[package]]
433name = "rustversion"
434version = "1.0.5"
435source = "registry+https://github.com/rust-lang/crates.io-index"
436checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
437
438[[package]]
439name = "serde"
440version = "1.0.132"
441source = "registry+https://github.com/rust-lang/crates.io-index"
442checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
443dependencies = [
444 "serde_derive",
445]
446
447[[package]]
448name = "serde_derive"
449version = "1.0.132"
450source = "registry+https://github.com/rust-lang/crates.io-index"
451checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
452dependencies = [
453 "proc-macro2",
454 "quote",
455 "syn",
456]
457
458[[package]]
459name = "slotmap"
460version = "1.0.6"
461source = "registry+https://github.com/rust-lang/crates.io-index"
462checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
463dependencies = [
464 "version_check",
465]
466
467[[package]]
468name = "strsim"
469version = "0.10.0"
470source = "registry+https://github.com/rust-lang/crates.io-index"
471checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
472
473[[package]]
474name = "syn"
475version = "1.0.81"
476source = "registry+https://github.com/rust-lang/crates.io-index"
477checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
478dependencies = [
479 "proc-macro2",
480 "quote",
481 "unicode-xid",
482]
483
484[[package]]
485name = "tempfile"
486version = "3.2.0"
487source = "registry+https://github.com/rust-lang/crates.io-index"
488checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
489dependencies = [
490 "cfg-if",
491 "libc",
492 "rand",
493 "redox_syscall",
494 "remove_dir_all",
495 "winapi",
496]
497
498[[package]]
499name = "termcolor"
500version = "1.1.2"
501source = "registry+https://github.com/rust-lang/crates.io-index"
502checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
503dependencies = [
504 "winapi-util",
505]
506
507[[package]]
508name = "textwrap"
509version = "0.14.2"
510source = "registry+https://github.com/rust-lang/crates.io-index"
511checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
512
513[[package]]
514name = "thiserror"
515version = "1.0.30"
516source = "registry+https://github.com/rust-lang/crates.io-index"
517checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
518dependencies = [
519 "thiserror-impl",
520]
521
522[[package]]
523name = "thiserror-impl"
524version = "1.0.30"
525source = "registry+https://github.com/rust-lang/crates.io-index"
526checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
527dependencies = [
528 "proc-macro2",
529 "quote",
530 "syn",
531]
532
533[[package]]
534name = "time"
535version = "0.3.5"
536source = "registry+https://github.com/rust-lang/crates.io-index"
537checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
538dependencies = [
539 "itoa",
540 "libc",
541 "time-macros",
542]
543
544[[package]]
545name = "time-macros"
546version = "0.2.3"
547source = "registry+https://github.com/rust-lang/crates.io-index"
548checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
549
550[[package]]
551name = "toml"
552version = "0.5.8"
553source = "registry+https://github.com/rust-lang/crates.io-index"
554checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
555dependencies = [
556 "serde",
557]
558
559[[package]]
560name = "treeline"
561version = "0.1.0"
562source = "registry+https://github.com/rust-lang/crates.io-index"
563checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
564
565[[package]]
566name = "unicode-segmentation"
567version = "1.8.0"
568source = "registry+https://github.com/rust-lang/crates.io-index"
569checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
570
571[[package]]
572name = "unicode-xid"
573version = "0.2.2"
574source = "registry+https://github.com/rust-lang/crates.io-index"
575checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
576
577[[package]]
578name = "version_check"
579version = "0.9.3"
580source = "registry+https://github.com/rust-lang/crates.io-index"
581checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
582
583[[package]]
584name = "wait-timeout"
585version = "0.2.0"
586source = "registry+https://github.com/rust-lang/crates.io-index"
587checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
588dependencies = [
589 "libc",
590]
591
592[[package]]
593name = "wasi"
594version = "0.10.0+wasi-snapshot-preview1"
595source = "registry+https://github.com/rust-lang/crates.io-index"
596checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
597
598[[package]]
599name = "winapi"
600version = "0.3.9"
601source = "registry+https://github.com/rust-lang/crates.io-index"
602checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
603dependencies = [
604 "winapi-i686-pc-windows-gnu",
605 "winapi-x86_64-pc-windows-gnu",
606]
607
608[[package]]
609name = "winapi-i686-pc-windows-gnu"
610version = "0.4.0"
611source = "registry+https://github.com/rust-lang/crates.io-index"
612checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
613
614[[package]]
615name = "winapi-util"
616version = "0.1.5"
617source = "registry+https://github.com/rust-lang/crates.io-index"
618checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
619dependencies = [
620 "winapi",
621]
622
623[[package]]
624name = "winapi-x86_64-pc-windows-gnu"
625version = "0.4.0"
626source = "registry+https://github.com/rust-lang/crates.io-index"
627checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644
index 76e6a10..0000000
--- a/Cargo.toml
+++ /dev/null
@@ -1,2 +0,0 @@
1[workspace]
2members = ["dotup", "dotup_cli"]
diff --git a/dotup/Cargo.toml b/dotup/Cargo.toml
deleted file mode 100644
index fe4876c..0000000
--- a/dotup/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
1[package]
2edition = "2018"
3name = "dotup"
4version = "0.1.0"
5
6[dependencies]
7log = "0.4"
8serde = { version = "*", features = ["derive"] }
9thiserror = "1.0"
10toml = "0.5"
11slotmap = "1.0"
12
13[dev-dependencies]
14tempfile = "3.2"
diff --git a/dotup/src/archive.rs b/dotup/src/archive.rs
deleted file mode 100644
index 7328b5b..0000000
--- a/dotup/src/archive.rs
+++ /dev/null
@@ -1,44 +0,0 @@
1use serde::{Deserialize, Serialize};
2use std::path::{Path, PathBuf};
3
4use crate::internal_prelude::*;
5
6#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct ArchiveLink {
8 pub origin: PathBuf,
9 pub destination: PathBuf,
10}
11
12#[derive(Debug, Default, Clone, Serialize, Deserialize)]
13pub struct Archive {
14 pub links: Vec<ArchiveLink>,
15}
16
17pub fn archive_exists(path: impl AsRef<Path>) -> bool {
18 utils::is_file(path).unwrap_or_default()
19}
20
21pub fn archive_read(path: impl AsRef<Path>) -> Result<Archive> {
22 let contents = std::fs::read_to_string(path)?;
23 archive_deserialize(contents)
24}
25
26pub fn archive_write(path: impl AsRef<Path>, archive: &Archive) -> Result<()> {
27 let serialized = archive_serialize(archive)?;
28 std::fs::write(path, &serialized)?;
29 Ok(())
30}
31
32pub fn archive_serialize(archive: &Archive) -> Result<String> {
33 match toml::to_string_pretty(archive) {
34 Ok(serialized) => Ok(serialized),
35 Err(e) => Err(Error::SerializationError(Box::new(e))),
36 }
37}
38
39pub fn archive_deserialize(contents: impl AsRef<str>) -> Result<Archive> {
40 match toml::from_str(contents.as_ref()) {
41 Ok(archive) => Ok(archive),
42 Err(e) => Err(Error::SerializationError(Box::new(e))),
43 }
44}
diff --git a/dotup/src/depot.rs b/dotup/src/depot.rs
deleted file mode 100644
index 658c0f6..0000000
--- a/dotup/src/depot.rs
+++ /dev/null
@@ -1,484 +0,0 @@
1use slotmap::SlotMap;
2use std::{
3 collections::HashMap,
4 fs::Metadata,
5 path::{Path, PathBuf},
6};
7use thiserror::Error;
8
9use crate::{internal_prelude::*, Archive, ArchiveLink};
10
11#[derive(Debug, Error)]
12pub enum LinkCreateError {
13 #[error("Link origin is outside depot base\nDepot : {}\nLink : {}", .depot_base.display(), .origin.display())]
14 LinkOriginOutsideDepot {
15 depot_base: PathBuf,
16 origin: PathBuf,
17 },
18 #[error("Link path is not relative : {}", .0.display())]
19 LinkPathIsNotRelative(PathBuf),
20 #[error("Link origin doesnt exist : {}", .0.display())]
21 LinkOriginDoesntExist(PathBuf),
22 #[error("Cannot create link for directory {} beacause it has a linked child", .0.display())]
23 DirectoryHasLinkedChildren(PathBuf),
24 #[error("Cannot create link for file {} beacause it has a linked parent", .0.display())]
25 FileHasLinkedParent(PathBuf),
26 #[error(transparent)]
27 IOError(#[from] std::io::Error),
28}
29
30#[derive(Debug, Error)]
31pub enum LinkInstallError {
32 #[error(transparent)]
33 IOError(#[from] std::io::Error),
34 #[error("File already exists at {}", .0.display())]
35 FileExists(PathBuf, Metadata),
36 /// .0 = LinkPath , .1 = LinkDestination
37 #[error("Link already exists {} -> {}", .0.display(), .1.display())]
38 LinkExists(PathBuf, PathBuf),
39}
40
41#[derive(Debug)]
42pub struct DepotConfig {
43 /// The archive used to initialize the depot.
44 /// A default archive can be create if one didnt already exist.
45 pub archive: Archive,
46 /// Path to the archive file. This path must be valid and must exist.
47 pub archive_path: PathBuf,
48}
49
50slotmap::new_key_type! { pub struct LinkID; }
51
52#[derive(Debug)]
53pub struct LinkCreateParams {
54 pub origin: PathBuf,
55 /// This must be a relative path
56 pub destination: PathBuf,
57}
58
59impl LinkCreateParams {
60 pub fn new(origin: impl Into<PathBuf>, destination: impl Into<PathBuf>) -> Self {
61 Self {
62 origin: origin.into(),
63 destination: destination.into(),
64 }
65 }
66}
67
68impl std::fmt::Display for LinkCreateParams {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 write!(
71 f,
72 "{} -> {}",
73 self.origin.display(),
74 self.destination.display()
75 )
76 }
77}
78
79impl From<ArchiveLink> for LinkCreateParams {
80 fn from(archive_link: ArchiveLink) -> Self {
81 Self {
82 origin: archive_link.origin,
83 destination: archive_link.destination,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89enum LinkType {
90 File,
91 Directory,
92}
93
94#[derive(Debug)]
95pub struct Link {
96 id: LinkID,
97 ty: LinkType,
98 /// The origin path, when joined with the depot base path, must be valid and it point to a file that exists.
99 origin: PathBuf,
100 /// Canonical version of origin
101 origin_canonical: PathBuf,
102 /// The destination path has to be a relative path.
103 /// To install a link the destination path is joined with the
104 /// install path and the file at base path + origin path is linked
105 /// to this resulting destination path.
106 destination: PathBuf,
107}
108
109impl Link {
110 pub fn id(&self) -> LinkID {
111 self.id
112 }
113
114 fn link_type(&self) -> LinkType {
115 self.ty
116 }
117
118 /// The relative path to the origin file. Relative from depot folder.
119 pub fn origin(&self) -> &Path {
120 &self.origin
121 }
122
123 pub fn origin_canonical(&self) -> &Path {
124 &self.origin_canonical
125 }
126
127 /// The relative path to the install destination.
128 /// This path should be concatenated with an install destination to get the actual destination
129 /// for this link.
130 pub fn destination(&self) -> &Path {
131 &self.destination
132 }
133
134 /// Where this link would be installed with the given `install_base`.
135 pub fn install_destination(&self, install_base: &Path) -> PathBuf {
136 utils::weakly_canonical(install_base.join(self.destination()))
137 }
138}
139
140impl std::fmt::Display for Link {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 write!(
143 f,
144 "{} -> {}",
145 self.origin().display(),
146 self.destination().display()
147 )
148 }
149}
150
151#[derive(Debug)]
152pub struct Depot {
153 // Must be canonical path
154 base_path: PathBuf,
155 // Must be canonical path
156 archive_path: PathBuf,
157 // Maps the origin to the link
158 links: SlotMap<LinkID, Link>,
159 links_by_origin: HashMap<PathBuf, LinkID>,
160}
161
162impl Depot {
163 /// Creates a new [`Depot`] using the config.
164 /// Fails if any of the links in the provided archive fail to be created.
165 pub fn new(config: DepotConfig) -> Result<Self> {
166 depot_create(config)
167 }
168
169 /// Creates a new link from the description.
170 /// The origin path must exist.
171 /// If a link with the same origin already existed then it is replaced.
172 pub fn create_link(&mut self, link_desc: LinkCreateParams) -> Result<LinkID, LinkCreateError> {
173 let link = depot_create_link(self, link_desc)?;
174 let link_id = depot_insert_link(self, link);
175 Ok(link_id)
176 }
177
178 pub fn get_link(&self, link_id: LinkID) -> Option<&Link> {
179 depot_get_link(self, link_id)
180 }
181
182 pub fn get_link_by_path(&self, path: &Path) -> Option<&Link> {
183 self.get_link_id_by_path(path)
184 .map(|id| self.get_link(id).unwrap())
185 }
186
187 pub fn get_link_id_by_path(&self, path: &Path) -> Option<LinkID> {
188 let weak = utils::weakly_canonical(path);
189 for link in self.links() {
190 if link.origin_canonical() == weak {
191 return Some(link.id());
192 }
193 }
194 None
195 }
196
197 pub fn rename_link(&mut self, link_id: LinkID, origin: &Path) {
198 // TODO: improve
199 if let Some(id) = self.get_link_id_by_path(origin) {
200 if link_id != id {
201 self.remove_link(id);
202 }
203 }
204 if let Some(link) = self.links.get_mut(link_id) {
205 let origin_canonical = utils::weakly_canonical(origin);
206 if !origin_canonical.starts_with(&self.base_path) {
207 panic!("new origin outside depot");
208 }
209
210 link.origin = origin_canonical;
211 link.origin_canonical = utils::weakly_canonical(&link.origin);
212 }
213 }
214
215 /// checks if there are any linked files/directories under the path `path`
216 /// if `path` is a path to a file and that file is linked then this function returns true
217 pub fn subpath_has_links(&self, path: &Path) -> bool {
218 let canonical = utils::weakly_canonical(path);
219 for link in self.links() {
220 if link.origin_canonical().starts_with(&canonical) {
221 return true;
222 }
223 }
224 false
225 }
226
227 pub fn remove_link(&mut self, link_id: LinkID) {
228 depot_remove_link(self, link_id)
229 }
230
231 pub fn remove_link_by_path(&mut self, path: &Path) {
232 if let Some(id) = self.get_link_id_by_path(path) {
233 self.remove_link(id);
234 }
235 }
236
237 /// Archives this depot so it can be serialized
238 pub fn archive(&self) -> Archive {
239 depot_archive(self)
240 }
241
242 pub fn links(&self) -> impl Iterator<Item = &Link> {
243 depot_links(self)
244 }
245
246 pub fn install_link(
247 &self,
248 link: &Link,
249 install_base: impl AsRef<Path>,
250 ) -> Result<(), LinkInstallError> {
251 depot_install_link(self, link, install_base.as_ref())
252 }
253
254 pub fn is_link_installed(&self, link: &Link, install_base: impl AsRef<Path>) -> bool {
255 depot_is_link_installed(link, install_base.as_ref())
256 }
257
258 pub fn uninstall_link(&self, link: &Link, install_base: impl AsRef<Path>) -> Result<()> {
259 depot_uninstall_link(self, link, install_base.as_ref())
260 }
261
262 pub fn base_path(&self) -> &Path {
263 &self.base_path
264 }
265
266 pub fn archive_path(&self) -> &Path {
267 &self.archive_path
268 }
269}
270
271fn depot_create(config: DepotConfig) -> Result<Depot> {
272 let archive_path = match config.archive_path.canonicalize() {
273 Ok(canonicalized) => canonicalized,
274 Err(e) => return Err(Error::ArchiveMissing(config.archive_path, e)),
275 };
276 if !archive_path.is_file() {
277 return Err(Error::ArchivePathNotFile(archive_path));
278 }
279 let base_path = archive_path
280 .parent()
281 .expect("Failed to get parent of archive path")
282 .to_path_buf();
283
284 let mut depot = Depot {
285 base_path,
286 archive_path,
287 links: Default::default(),
288 links_by_origin: Default::default(),
289 };
290
291 for archive_link in config.archive.links {
292 let link_desc = LinkCreateParams::from(archive_link);
293 let link = depot_create_link(&depot, link_desc)?;
294 depot_insert_link(&mut depot, link);
295 }
296
297 Ok(depot)
298}
299
300fn depot_archive(depot: &Depot) -> Archive {
301 let mut links = Vec::new();
302
303 for link in depot_links(depot) {
304 let archive_link = link_to_archive_link(link);
305 links.push(archive_link);
306 }
307
308 Archive { links }
309}
310
311/// Create a valid link for that given Depot using the given link desc.
312/// The link id is corrected when the link is inserted in the depot.
313fn depot_create_link(depot: &Depot, link_desc: LinkCreateParams) -> Result<Link, LinkCreateError> {
314 // link_ensure_relative_path(&link_desc.origin)?;
315 link_ensure_relative_path(&link_desc.destination)?;
316 debug_assert!(utils::is_canonical(depot.base_path()));
317
318 // Check if the file/directory at origin actually exists
319 let origin_joined = depot.base_path().join(&link_desc.origin);
320 let origin_result = origin_joined.canonicalize();
321 let origin_canonical = match origin_result {
322 Ok(canonical) => canonical,
323 Err(e) => match e.kind() {
324 std::io::ErrorKind::NotFound => {
325 return Err(LinkCreateError::LinkOriginDoesntExist(origin_joined))
326 }
327 _ => return Err(e.into()),
328 },
329 };
330
331 if !origin_canonical.starts_with(depot.base_path()) {
332 return Err(LinkCreateError::LinkOriginOutsideDepot {
333 depot_base: depot.base_path().to_path_buf(),
334 origin: origin_canonical,
335 });
336 }
337
338 // unwrap should be fine, this path starts with the prefix
339 let origin = origin_canonical
340 .strip_prefix(depot.base_path())
341 .unwrap()
342 .to_path_buf();
343 let destination = link_desc.destination;
344
345 let ty = if origin.is_dir() {
346 for link in depot.links() {
347 if link.origin().starts_with(&origin) && link.origin() != origin {
348 return Err(LinkCreateError::DirectoryHasLinkedChildren(origin));
349 }
350 }
351 LinkType::Directory
352 } else {
353 for link in depot.links() {
354 if origin.starts_with(link.origin()) && origin != link.origin() {
355 assert_eq!(link.link_type(), LinkType::Directory);
356 return Err(LinkCreateError::FileHasLinkedParent(origin));
357 }
358 }
359 LinkType::File
360 };
361
362 Ok(Link {
363 id: Default::default(),
364 ty,
365 origin,
366 origin_canonical,
367 destination,
368 })
369}
370
371fn depot_get_link(depot: &Depot, link_id: LinkID) -> Option<&Link> {
372 depot.links.get(link_id)
373}
374
375fn depot_remove_link(depot: &mut Depot, link_id: LinkID) {
376 depot.links.remove(link_id);
377}
378
379fn depot_install_link(
380 _depot: &Depot,
381 link: &Link,
382 install_base: &Path,
383) -> Result<(), LinkInstallError> {
384 let final_origin = link.origin_canonical();
385 let final_destination = link.install_destination(install_base);
386
387 log::debug!("Final origin : {}", final_origin.display());
388 log::debug!("Final destination : {}", final_destination.display());
389
390 if let Some(dest_base) = final_destination.parent() {
391 std::fs::create_dir_all(dest_base)?;
392 }
393
394 // Exit early if there is some error or if the link already exists
395 match std::fs::symlink_metadata(&final_destination) {
396 Ok(metadata) => {
397 let filetype = metadata.file_type();
398 if filetype.is_symlink() {
399 let symlink_destination = std::fs::read_link(&final_destination)?;
400 if symlink_destination == final_origin {
401 return Ok(());
402 }
403 log::trace!(
404 "Symlink destinations where not equal : {} != {}",
405 final_origin.display(),
406 symlink_destination.display()
407 );
408 return Err(LinkInstallError::LinkExists(
409 final_destination,
410 symlink_destination,
411 ));
412 } else {
413 return Err(LinkInstallError::FileExists(final_destination, metadata));
414 }
415 }
416 Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
417 Err(e) => return Err(e.into()),
418 };
419
420 log::debug!(
421 "Creating symlink from {} to {}",
422 final_origin.display(),
423 final_destination.display()
424 );
425 std::os::unix::fs::symlink(&final_origin, &final_destination)?;
426
427 Ok(())
428}
429
430fn depot_is_link_installed(link: &Link, install_base: &Path) -> bool {
431 let origin_canonical = link.origin_canonical();
432 let install_destination = link.install_destination(install_base);
433 match std::fs::read_link(&install_destination) {
434 Ok(target) if target == origin_canonical => true,
435 _ => false,
436 }
437}
438
439fn depot_uninstall_link(_depot: &Depot, link: &Link, install_base: &Path) -> Result<()> {
440 let origin_canonical = link.origin_canonical();
441 let install_destination = link.install_destination(install_base);
442 let link_target = match std::fs::read_link(&install_destination) {
443 Ok(target) => target,
444 Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(()),
445 Err(e) => return Err(e.into()),
446 };
447
448 if link_target.canonicalize()? == origin_canonical {
449 std::fs::remove_file(&install_destination)?;
450 }
451
452 Ok(())
453}
454
455fn depot_insert_link(depot: &mut Depot, mut link: Link) -> LinkID {
456 let origin = link.origin().to_path_buf();
457 if let Some(link_id) = depot.links_by_origin.remove(&origin) {
458 depot.links.remove(link_id);
459 }
460 let link_id = depot.links.insert_with_key(move |k| {
461 link.id = k;
462 link
463 });
464 depot.links_by_origin.insert(origin, link_id);
465 link_id
466}
467
468fn depot_links(depot: &Depot) -> impl Iterator<Item = &Link> {
469 depot.links.values()
470}
471
472fn link_ensure_relative_path(path: &Path) -> Result<(), LinkCreateError> {
473 if !path.is_relative() {
474 return Err(LinkCreateError::LinkPathIsNotRelative(path.to_path_buf()));
475 }
476 Ok(())
477}
478
479fn link_to_archive_link(depot_link: &Link) -> ArchiveLink {
480 ArchiveLink {
481 origin: depot_link.origin().to_path_buf(),
482 destination: depot_link.destination().to_path_buf(),
483 }
484}
diff --git a/dotup/src/error.rs b/dotup/src/error.rs
deleted file mode 100644
index 8ad801d..0000000
--- a/dotup/src/error.rs
+++ /dev/null
@@ -1,21 +0,0 @@
1use crate::{LinkCreateError, LinkInstallError};
2use std::path::PathBuf;
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum Error {
7 #[error("Link install error : {0}")]
8 LinkInstallError(#[from] LinkInstallError),
9 #[error("Link create error : {0}")]
10 LinkCreateError(#[from] LinkCreateError),
11 #[error("Link origin doesnt exist : {}", .0.display())]
12 ArchivePathNotFile(PathBuf),
13 #[error("The archive path did not exist : {}\n{}", .0.display(), .1)]
14 ArchiveMissing(PathBuf, std::io::Error),
15 #[error("Deserialization error : {0}")]
16 SerializationError(Box<dyn std::error::Error + Send + Sync + 'static>),
17 #[error(transparent)]
18 IOError(#[from] std::io::Error),
19}
20
21pub type Result<T, E = Error> = std::result::Result<T, E>;
diff --git a/dotup/src/lib.rs b/dotup/src/lib.rs
deleted file mode 100644
index 96c4cd7..0000000
--- a/dotup/src/lib.rs
+++ /dev/null
@@ -1,18 +0,0 @@
1mod archive;
2mod depot;
3mod error;
4
5pub mod utils;
6
7pub use archive::{
8 archive_deserialize, archive_exists, archive_read, archive_serialize, archive_write, Archive,
9 ArchiveLink,
10};
11pub use depot::{
12 Depot, DepotConfig, Link, LinkCreateError, LinkCreateParams, LinkID, LinkInstallError,
13};
14pub use error::{Error, Result};
15
16pub(crate) mod internal_prelude {
17 pub use super::{utils, Error, Result};
18}
diff --git a/dotup/src/utils.rs b/dotup/src/utils.rs
deleted file mode 100644
index c9ea959..0000000
--- a/dotup/src/utils.rs
+++ /dev/null
@@ -1,94 +0,0 @@
1use std::path::{Component, Path, PathBuf};
2
3pub fn is_file(path: impl AsRef<Path>) -> std::io::Result<bool> {
4 let metadata = match std::fs::metadata(path) {
5 Ok(metadata) => metadata,
6 Err(e) => match e.kind() {
7 std::io::ErrorKind::NotFound => return Ok(false),
8 _ => return Err(e),
9 },
10 };
11 Ok(metadata.is_file())
12}
13
14pub fn is_directory(path: impl AsRef<Path>) -> std::io::Result<bool> {
15 let metadata = match std::fs::metadata(path) {
16 Ok(metadata) => metadata,
17 Err(e) => match e.kind() {
18 std::io::ErrorKind::NotFound => return Ok(false),
19 _ => return Err(e),
20 },
21 };
22 Ok(metadata.is_dir())
23}
24
25pub fn is_canonical(path: &Path) -> bool {
26 path == weakly_canonical(path).as_path()
27}
28
29pub fn weakly_canonical(path: impl AsRef<Path>) -> PathBuf {
30 let cwd = std::env::current_dir().expect("Failed to obtain current directory");
31 weakly_canonical_cwd(path, cwd)
32}
33
34fn weakly_canonical_cwd(path: impl AsRef<Path>, cwd: PathBuf) -> PathBuf {
35 // Adapated from
36 // https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61
37 let path = path.as_ref();
38
39 let mut components = path.components().peekable();
40 let mut canonical = cwd;
41 let prefix = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
42 components.next();
43 PathBuf::from(c.as_os_str())
44 } else {
45 PathBuf::new()
46 };
47
48 for component in components {
49 match component {
50 Component::Prefix(_) => unreachable!(),
51 Component::RootDir => {
52 canonical = prefix.clone();
53 canonical.push(component.as_os_str())
54 }
55 Component::CurDir => {}
56 Component::ParentDir => {
57 canonical.pop();
58 }
59 Component::Normal(p) => canonical.push(p),
60 };
61 }
62
63 canonical
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn weak_canonical_test() {
72 let cwd = PathBuf::from("/home/user");
73 assert_eq!(
74 PathBuf::from("/home/dest"),
75 weakly_canonical_cwd("../dest", cwd.clone())
76 );
77 assert_eq!(
78 PathBuf::from("/home/dest/configs/init.vim"),
79 weakly_canonical_cwd("../dest/configs/init.vim", cwd.clone())
80 );
81 assert_eq!(
82 PathBuf::from("/dest/configs/init.vim"),
83 weakly_canonical_cwd("/dest/configs/init.vim", cwd.clone())
84 );
85 assert_eq!(
86 PathBuf::from("/home/user/configs/nvim/lua/setup.lua"),
87 weakly_canonical_cwd("./configs/nvim/lua/setup.lua", cwd.clone())
88 );
89 assert_eq!(
90 PathBuf::from("/home/user/configs/nvim/lua/setup.lua"),
91 weakly_canonical_cwd("configs/nvim/lua/setup.lua", cwd.clone())
92 );
93 }
94}
diff --git a/dotup/tests/integration_tests.rs b/dotup/tests/integration_tests.rs
deleted file mode 100644
index d6bed8d..0000000
--- a/dotup/tests/integration_tests.rs
+++ /dev/null
@@ -1,126 +0,0 @@
1use std::path::{Path, PathBuf};
2
3use dotup::{ArchiveLink, Depot, DepotConfig, LinkCreateParams};
4use tempfile::TempDir;
5
6const TESTING_DEPOT_NAME: &str = "depot.toml";
7const TESTING_DEPOT_CONTENTS: &str = include_str!("testing_depot.toml");
8
9fn create_empty_file(path: impl AsRef<Path>) {
10 let path = path.as_ref();
11 if let Some(parent) = path.parent() {
12 std::fs::create_dir_all(parent).unwrap();
13 }
14 std::fs::write(path, "").unwrap();
15}
16
17fn prepare_empty_temp_dir() -> TempDir {
18 TempDir::new().unwrap()
19}
20
21fn prepare_temp_dir() -> TempDir {
22 let dir = TempDir::new().unwrap();
23 std::fs::write(dir.path().join(TESTING_DEPOT_NAME), TESTING_DEPOT_CONTENTS).unwrap();
24 create_empty_file(dir.path().join("o1/file1.txt"));
25 create_empty_file(dir.path().join("o2/file2.txt"));
26 dir
27}
28
29fn read_depot(dir: &TempDir) -> Depot {
30 let archive_path = dir.path().join(TESTING_DEPOT_NAME);
31 Depot::new(DepotConfig {
32 archive: dotup::archive_read(&archive_path).unwrap(),
33 archive_path,
34 })
35 .unwrap()
36}
37
38#[test]
39fn test_archive_deserialize() {
40 let archive = dotup::archive_deserialize(&TESTING_DEPOT_CONTENTS).unwrap();
41
42 let link1 = ArchiveLink {
43 origin: PathBuf::from("o1/file1.txt"),
44 destination: PathBuf::from("d1/file.txt"),
45 };
46 let link2 = ArchiveLink {
47 origin: PathBuf::from("o2/file2.txt"),
48 destination: PathBuf::from("d2/d2/file.txt"),
49 };
50
51 assert_eq!(2, archive.links.len());
52 assert!(archive.links.contains(&link1));
53 assert!(archive.links.contains(&link2));
54}
55
56#[test]
57fn test_archive_exists() {
58 let empty_dir = prepare_empty_temp_dir();
59 let dir = prepare_temp_dir();
60
61 assert!(!dotup::archive_exists(
62 empty_dir.path().join(TESTING_DEPOT_NAME)
63 ));
64 assert!(dotup::archive_exists(dir.path().join(TESTING_DEPOT_NAME)));
65}
66
67#[test]
68fn test_depot_create() {
69 let empty_dir = prepare_empty_temp_dir();
70 let dir = prepare_temp_dir();
71
72 let d1 = Depot::new(DepotConfig {
73 archive: Default::default(),
74 archive_path: empty_dir.path().join(TESTING_DEPOT_NAME),
75 });
76 assert!(d1.is_err());
77
78 let archive_path = dir.path().join(TESTING_DEPOT_NAME);
79 let d2 = Depot::new(DepotConfig {
80 archive: dotup::archive_read(&archive_path).unwrap(),
81 archive_path,
82 });
83 assert!(d2.is_ok());
84}
85
86#[test]
87fn test_depot_create_link() {
88 let dir = prepare_temp_dir();
89 let mut depot = read_depot(&dir);
90
91 create_empty_file(dir.path().join("o3/file.txt"));
92
93 let l1 = depot.create_link(LinkCreateParams {
94 origin: PathBuf::from("o3/file.txt"),
95 destination: PathBuf::from(".config/file.txt"),
96 });
97 assert!(l1.is_ok());
98
99 let l2 = depot.create_link(LinkCreateParams {
100 origin: PathBuf::from("o4/file.txt"),
101 destination: PathBuf::from(".config/file.txt"),
102 });
103 assert!(l2.is_err());
104}
105
106#[test]
107fn test_depot_install_uninstall_link() {
108 let dir = prepare_temp_dir();
109 let depot = read_depot(&dir);
110 let install_base = dir.path();
111
112 for link in depot.links() {
113 depot.install_link(link, install_base).unwrap();
114 }
115
116 for link in depot.links() {
117 let link_path = link.install_destination(install_base);
118 let link_target = std::fs::read_link(&link_path).unwrap();
119 assert_eq!(link_target.canonicalize().unwrap(), link.origin_canonical());
120 }
121
122 for link in depot.links() {
123 depot.uninstall_link(link, install_base).unwrap();
124 assert!(!link.install_destination(install_base).exists());
125 }
126}
diff --git a/dotup/tests/testing_depot.toml b/dotup/tests/testing_depot.toml
deleted file mode 100644
index ee5224a..0000000
--- a/dotup/tests/testing_depot.toml
+++ /dev/null
@@ -1,7 +0,0 @@
1[[links]]
2origin = "o1/file1.txt"
3destination = "d1/file.txt"
4
5[[links]]
6origin = "o2/file2.txt"
7destination = "d2/d2/file.txt"
diff --git a/dotup_cli/Cargo.toml b/dotup_cli/Cargo.toml
deleted file mode 100644
index 89b8c27..0000000
--- a/dotup_cli/Cargo.toml
+++ /dev/null
@@ -1,29 +0,0 @@
1[package]
2edition = "2018"
3name = "dotup_cli"
4version = "0.1.0"
5
6[[bin]]
7name = "dotup"
8path = "src/main.rs"
9doc = false
10
11[dependencies]
12anyhow = "1.0"
13log = "0.4"
14ansi_term = "0.12.1"
15
16[dependencies.clap]
17features = ["derive"]
18version = "3.0.0-rc.7"
19
20[dependencies.dotup]
21path = "../dotup"
22
23[dependencies.flexi_logger]
24features = ["colors"]
25version = "0.22"
26
27[dev-dependencies]
28tempfile = "3.2"
29assert_cmd = "2.0"
diff --git a/dotup_cli/src/commands/init.rs b/dotup_cli/src/commands/init.rs
deleted file mode 100644
index 45129bf..0000000
--- a/dotup_cli/src/commands/init.rs
+++ /dev/null
@@ -1,22 +0,0 @@
1use super::prelude::*;
2
3/// Creates an empty depot file if one doesnt already exist.
4///
5/// By default this will create the file in the current directory
6/// but the --depot flag can be used to change this path.
7#[derive(Parser)]
8pub struct Opts {}
9
10pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
11 if !dotup::utils::is_file(&config.archive_path)? {
12 let archive = Archive::default();
13 log::info!("Creating archive at {}", &config.archive_path.display());
14 utils::write_archive(&config.archive_path, &archive)?;
15 } else {
16 log::warn!(
17 "Archive file already exists : '{}'",
18 config.archive_path.display()
19 );
20 }
21 Ok(())
22}
diff --git a/dotup_cli/src/commands/install.rs b/dotup_cli/src/commands/install.rs
deleted file mode 100644
index 6d9fbf7..0000000
--- a/dotup_cli/src/commands/install.rs
+++ /dev/null
@@ -1,25 +0,0 @@
1use std::path::PathBuf;
2
3use super::prelude::*;
4
5/// Install links. (Creates symlinks).
6///
7/// Installing a link will create the necessary directories.
8/// If a file or directory already exists at the location a link would be installed this command will fail.
9#[derive(Parser)]
10pub struct Opts {
11 /// The files/directories to install.
12 #[clap(min_values = 1, default_value = ".")]
13 paths: Vec<PathBuf>,
14}
15
16pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
17 let depot = utils::read_depot(&config.archive_path)?;
18
19 for link in utils::collect_links_by_base_paths(&depot, &opts.paths) {
20 log::info!("Installing link {}", link);
21 depot.install_link(link, &config.install_path)?;
22 }
23
24 Ok(())
25}
diff --git a/dotup_cli/src/commands/link.rs b/dotup_cli/src/commands/link.rs
deleted file mode 100644
index d1f61ea..0000000
--- a/dotup_cli/src/commands/link.rs
+++ /dev/null
@@ -1,134 +0,0 @@
1use std::{
2 fs::{DirEntry, Metadata},
3 path::{Path, PathBuf},
4};
5
6use super::prelude::*;
7
8/// Creates links
9///
10/// If a link is created for a file that already had a link then the old link will be overwritten.
11/// By default creating a link to a directory will recursively link all files under that
12/// directory, to actually link a directory use the --directory flag.
13#[derive(Parser)]
14pub struct Opts {
15 /// Treats the paths as directories. This will create links to the actual directories instead
16 /// of recursively linking all files under them.
17 #[clap(long)]
18 directory: bool,
19
20 /// The paths to link. The last path is the destination.
21 paths: Vec<PathBuf>,
22}
23
24// TODO: require destination
25// remove else branch
26pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
27 let mut depot = utils::read_depot(&config.archive_path)?;
28
29 let (origins, destination) = match opts.paths.as_slice() {
30 p @ [] | p @ [_] => (p, None),
31 [o @ .., dest] => (o, Some(dest)),
32 _ => unreachable!(),
33 };
34
35 if let Some(destination) = destination {
36 let params = if opts.directory {
37 origins
38 .iter()
39 .map(|p| LinkCreateParams {
40 origin: p.to_path_buf(),
41 destination: destination.clone(),
42 })
43 .collect()
44 } else {
45 let mut params = Vec::new();
46 for origin in origins {
47 generate_link_params(&depot, origin, destination, origin, &mut params)?;
48 }
49 params
50 };
51
52 for link_params in params {
53 log::info!("Creating link : {}", link_params);
54 depot.create_link(link_params)?;
55 }
56 } else {
57 let base_path = match origins {
58 [] => std::env::current_dir()?,
59 [path] => path.clone(),
60 _ => unreachable!(),
61 };
62
63 for link in utils::collect_links_by_base_paths(&depot, std::iter::once(base_path)) {
64 log::info!("{}", link);
65 }
66 }
67
68 utils::write_depot(&depot)?;
69
70 Ok(())
71}
72
73fn generate_link_params(
74 depot: &Depot,
75 origin: &Path,
76 destination: &Path,
77 base: &Path,
78 params: &mut Vec<LinkCreateParams>,
79) -> anyhow::Result<()> {
80 let metadata = std::fs::metadata(origin)?;
81 if metadata.is_file() {
82 generate_file_link_params(depot, origin, destination, base, params)?;
83 } else if metadata.is_dir() {
84 generate_directory_link_params_recursive(depot, origin, destination, base, params)?;
85 }
86 Ok(())
87}
88
89fn generate_file_link_params(
90 depot: &Depot,
91 origin: &Path,
92 destination: &Path,
93 base: &Path,
94 params: &mut Vec<LinkCreateParams>,
95) -> anyhow::Result<()> {
96 let origin_canonical = origin
97 .canonicalize()
98 .expect("Failed to canonicalize origin path");
99 let base_canonical = base
100 .canonicalize()
101 .expect("Failed to canonicalize base path");
102
103 log::debug!("Origin canonical : {}", origin_canonical.display());
104 log::debug!("Base : {}", base.display());
105
106 let partial = origin_canonical
107 .strip_prefix(base_canonical)
108 .expect("Failed to remove prefix from origin path");
109 let destination = destination.join(partial);
110 let origin = origin_canonical
111 .strip_prefix(depot.base_path())
112 .unwrap_or(&origin_canonical);
113
114 let link_params = LinkCreateParams {
115 origin: origin.to_path_buf(),
116 destination,
117 };
118 params.push(link_params);
119 Ok(())
120}
121
122fn generate_directory_link_params_recursive(
123 depot: &Depot,
124 dir_path: &Path,
125 destination: &Path,
126 base: &Path,
127 params: &mut Vec<LinkCreateParams>,
128) -> anyhow::Result<()> {
129 for origin in dir_path.read_dir()? {
130 let origin = origin?.path();
131 generate_link_params(depot, &origin, destination, base, params)?;
132 }
133 Ok(())
134}
diff --git a/dotup_cli/src/commands/mod.rs b/dotup_cli/src/commands/mod.rs
deleted file mode 100644
index bd92599..0000000
--- a/dotup_cli/src/commands/mod.rs
+++ /dev/null
@@ -1,11 +0,0 @@
1pub mod init;
2pub mod install;
3pub mod link;
4pub mod mv;
5pub mod status;
6pub mod uninstall;
7pub mod unlink;
8
9mod prelude {
10 pub use crate::prelude::*;
11}
diff --git a/dotup_cli/src/commands/mv.rs b/dotup_cli/src/commands/mv.rs
deleted file mode 100644
index aae2715..0000000
--- a/dotup_cli/src/commands/mv.rs
+++ /dev/null
@@ -1,53 +0,0 @@
1use std::path::{Path, PathBuf};
2
3use super::prelude::*;
4
5/// Install links. (Creates symlinks).
6///
7/// Installing a link will create the necessary directories.
8/// If a file or directory already exists at the location a link would be installed this command will fail.
9#[derive(Parser)]
10pub struct Opts {
11 /// The files/directories to move
12 #[clap(min_values = 2)]
13 paths: Vec<PathBuf>,
14}
15
16pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
17 let mut depot = utils::read_depot(&config.archive_path)?;
18
19 let (sources, destination) = match opts.paths.as_slice() {
20 [source, destination] => {}
21 [sources @ .., destination] => {
22 let mut curr_destination = destination.to_owned();
23 for source in sources {
24 let filename = match source.file_name() {
25 Some(filename) => filename,
26 None => {
27 log::warn!("Ignoring '{}', unknown file name", source.display());
28 continue;
29 }
30 };
31 curr_destination.push(filename);
32 std::fs::rename(source, &curr_destination)?;
33 if let Some(id) = depot.get_link_id_by_path(&source) {
34 depot.rename_link(id, &curr_destination);
35 }
36 curr_destination.pop();
37 }
38 }
39 _ => unreachable!(),
40 };
41
42 utils::write_depot(&depot)?;
43
44 Ok(())
45}
46
47fn rename(depot: &mut Depot, source: &Path, destination: &Path) -> anyhow::Result<()> {
48 std::fs::rename(source, &destination)?;
49 if let Some(id) = depot.get_link_id_by_path(&source) {
50 depot.rename_link(id, &destination);
51 }
52 Ok(())
53}
diff --git a/dotup_cli/src/commands/status.rs b/dotup_cli/src/commands/status.rs
deleted file mode 100644
index b7221db..0000000
--- a/dotup_cli/src/commands/status.rs
+++ /dev/null
@@ -1,175 +0,0 @@
1use std::path::{Path, PathBuf};
2
3use ansi_term::Colour;
4use clap::Parser;
5use dotup::{Depot, Link};
6
7use crate::{utils, Config};
8
9/// Shows information about links
10///
11/// If a link is created for a file that already had a link then the old link will be overwritten.
12/// By default creating a link to a directory will recursively link all files under that
13/// directory, to actually link a directory use the --directory flag.
14#[derive(Parser)]
15pub struct Opts {
16 /// The location where links will be installed to.
17 /// Defaults to the home directory.
18 #[clap(long)]
19 install_base: Option<PathBuf>,
20
21 /// The paths to show the status of
22 paths: Vec<PathBuf>,
23}
24
25#[derive(Debug)]
26struct State {
27 max_depth: u32,
28 install_path: PathBuf,
29}
30
31pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
32 let mut depot = utils::read_depot(&config.archive_path)?;
33
34 // walk dir
35 // if node is file:
36 // if linked
37 // print name in green and destination blue
38 // if invalid
39 // print name and destination red
40 // if not linked
41 // print name in gray
42 // if node is directory:
43 // if linked
44 // print name in green and destination blue
45 // if invalid
46 // print name and destination red
47 // if not linked:
48 // print name in gray
49 // if contains files that are linked/invalid:
50 // recurse into directory
51 //
52
53 let depot_base = depot.base_path();
54 let mut paths = Vec::new();
55 for path in opts.paths {
56 let canonical = dotup::utils::weakly_canonical(&path);
57 if canonical.starts_with(depot_base) {
58 paths.push(canonical);
59 } else {
60 log::warn!("Path '{}' is outside the depot", path.display());
61 }
62 }
63
64 if paths.is_empty() {
65 paths.push(PathBuf::from("."));
66 }
67
68 let state = State {
69 max_depth: u32::MAX,
70 install_path: config.install_path,
71 };
72
73 let (directories, files) = utils::collect_read_dir_split(".")?;
74 for path in directories.into_iter().chain(files.into_iter()) {
75 display_status_path(&depot, &state, &path, 0);
76 }
77
78 utils::write_depot(&depot)?;
79
80 Ok(())
81}
82
83fn display_status_path(depot: &Depot, state: &State, path: &Path, depth: u32) {
84 if depth == state.max_depth {
85 return;
86 }
87
88 if path.is_dir() {
89 display_status_directory(depot, state, path, depth);
90 } else {
91 display_status_file(depot, state, path, depth);
92 }
93}
94
95fn display_status_directory(depot: &Depot, state: &State, path: &Path, depth: u32) {
96 assert!(path.is_dir());
97 if depth == state.max_depth || !depot.subpath_has_links(path) {
98 return;
99 }
100
101 if let Some(link) = depot.get_link_by_path(path) {
102 print_link(depot, state, link, depth);
103 } else {
104 for entry in std::fs::read_dir(path).unwrap() {
105 let entry = match entry {
106 Ok(entry) => entry,
107 Err(_) => continue,
108 };
109 let entry_path = entry.path().canonicalize().unwrap();
110 let entry_path_stripped = entry_path
111 .strip_prefix(std::env::current_dir().unwrap())
112 .unwrap();
113
114 print_identation(depth);
115 println!(
116 "{}",
117 Colour::Yellow.paint(&format!("{}", path.file_name().unwrap().to_string_lossy()))
118 );
119
120 display_status_path(depot, state, &entry_path_stripped, depth + 1);
121 }
122 }
123}
124
125fn display_status_file(depot: &Depot, state: &State, path: &Path, depth: u32) {
126 print_identation(depth);
127 if let Some(link) = depot.get_link_by_path(path) {
128 print_link(depot, state, link, depth);
129 } else {
130 print_unlinked(path, depth);
131 }
132}
133
134fn print_link(depot: &Depot, state: &State, link: &Link, depth: u32) {
135 let origin = link.origin();
136 let dest = link.destination();
137 let filename = match origin.file_name() {
138 Some(filename) => filename,
139 None => return,
140 };
141
142 print_identation(depth);
143 if depot.is_link_installed(link, &state.install_path) {
144 println!(
145 "{} -> {}",
146 Colour::Green.paint(&format!("{}", filename.to_string_lossy())),
147 Colour::Blue.paint(&format!("{}", dest.display())),
148 );
149 } else {
150 println!(
151 "{}",
152 Colour::Red.paint(&format!(
153 "{} -> {}",
154 filename.to_string_lossy(),
155 dest.display()
156 ))
157 );
158 }
159}
160
161fn print_unlinked(path: &Path, depth: u32) {
162 let filename = match path.file_name() {
163 Some(filename) => filename,
164 None => return,
165 };
166
167 print_identation(depth);
168 println!("{}", filename.to_string_lossy());
169}
170
171fn print_identation(depth: u32) {
172 for i in 0..depth {
173 print!(" ");
174 }
175}
diff --git a/dotup_cli/src/commands/uninstall.rs b/dotup_cli/src/commands/uninstall.rs
deleted file mode 100644
index fe44bf0..0000000
--- a/dotup_cli/src/commands/uninstall.rs
+++ /dev/null
@@ -1,26 +0,0 @@
1use std::path::PathBuf;
2
3use super::prelude::*;
4
5/// Uninstalls links. (Removes symlinks).
6///
7/// Uninstalling a link for a file that didnt have a link will do nothing.
8/// Uninstalling a directory will recursively uninstall all files under it.
9/// Symlinks are only deleted if they were pointing to the correct file.
10#[derive(Parser)]
11pub struct Opts {
12 /// The files/directories to uninstall.
13 #[clap(min_values = 1, default_value = ".")]
14 paths: Vec<PathBuf>,
15}
16
17pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
18 let depot = utils::read_depot(&config.archive_path)?;
19
20 for link in utils::collect_links_by_base_paths(&depot, &opts.paths) {
21 log::info!("Uninstalling link : {}", link);
22 depot.uninstall_link(link, &config.install_path)?;
23 }
24
25 Ok(())
26}
diff --git a/dotup_cli/src/commands/unlink.rs b/dotup_cli/src/commands/unlink.rs
deleted file mode 100644
index abe23e3..0000000
--- a/dotup_cli/src/commands/unlink.rs
+++ /dev/null
@@ -1,43 +0,0 @@
1use std::{
2 fs::{DirEntry, Metadata},
3 path::{Path, PathBuf},
4};
5
6use super::prelude::*;
7
8/// Unlinks files/directories.
9///
10/// This will recursively remove links. If a path is a directory then it will remove all links
11/// recursively.
12/// The links are not uninstall by default, see the --uninstall parameter.
13#[derive(Parser)]
14pub struct Opts {
15 /// Specify the install base if the links are also to be uninstalled.
16 #[clap(long)]
17 uninstall: Option<PathBuf>,
18
19 /// The paths to unlink.
20 #[clap(required = true, min_values = 1)]
21 paths: Vec<PathBuf>,
22}
23
24pub fn main(config: Config, opts: Opts) -> anyhow::Result<()> {
25 let mut depot = utils::read_depot(&config.archive_path)?;
26
27 for link_id in utils::collect_link_ids_by_base_paths(&depot, &opts.paths) {
28 let link = depot.get_link(link_id).unwrap();
29 log::info!(
30 "Unlinking(uninstall = {}) : {}",
31 opts.uninstall.is_some(),
32 link
33 );
34 if let Some(ref install_base) = opts.uninstall {
35 depot.uninstall_link(link, &install_base)?;
36 }
37 depot.remove_link(link_id);
38 }
39
40 utils::write_depot(&depot)?;
41
42 Ok(())
43}
diff --git a/dotup_cli/src/config.rs b/dotup_cli/src/config.rs
deleted file mode 100644
index dabaf74..0000000
--- a/dotup_cli/src/config.rs
+++ /dev/null
@@ -1,10 +0,0 @@
1use std::path::PathBuf;
2
3#[derive(Debug)]
4pub struct Config {
5 pub archive_path: PathBuf,
6 pub install_path: PathBuf,
7 pub working_path: PathBuf,
8}
9
10impl Config {}
diff --git a/dotup_cli/src/main.rs b/dotup_cli/src/main.rs
deleted file mode 100644
index 0d730da..0000000
--- a/dotup_cli/src/main.rs
+++ /dev/null
@@ -1,111 +0,0 @@
1#![allow(unused)]
2
3pub mod commands;
4pub mod config;
5pub mod utils;
6
7pub use config::Config;
8
9pub mod prelude {
10 pub use super::{utils, Config};
11 pub use clap::{AppSettings, Parser};
12 pub use dotup::{Archive, Depot, DepotConfig, Link, LinkCreateParams, LinkID};
13}
14
15use clap::{AppSettings, Parser};
16use flexi_logger::Logger;
17use std::{
18 collections::HashMap,
19 iter::FromIterator,
20 path::{Path, PathBuf},
21};
22
23use prelude::*;
24
25#[derive(Parser)]
26struct Opts {
27 /// Path to the depot file.
28 ///
29 /// By default it will try to find a file named "depot.toml" in the current directory or any of
30 /// the parent directories.
31 #[clap(long)]
32 depot: Option<PathBuf>,
33
34 /// Disable output to the console
35 #[clap(short, long)]
36 quiet: bool,
37
38 /// A level of verbosity, and can be used multiple times
39 ///
40 /// Level 1 - Info
41 ///
42 /// Level 2 - Debug
43 ///
44 /// Level 3 - Trace
45 #[clap(short, long, parse(from_occurrences))]
46 verbose: i32,
47
48 /// The location where links will be installed to.
49 /// Defaults to the home directory.
50 #[clap(short, long)]
51 install_path: Option<PathBuf>,
52
53 #[clap(subcommand)]
54 subcmd: SubCommand,
55}
56
57#[derive(Parser)]
58enum SubCommand {
59 Init(commands::init::Opts),
60 Link(commands::link::Opts),
61 Mv(commands::mv::Opts),
62 Status(commands::status::Opts),
63 Unlink(commands::unlink::Opts),
64 Install(commands::install::Opts),
65 Uninstall(commands::uninstall::Opts),
66}
67
68fn main() -> anyhow::Result<()> {
69 let opts = Opts::parse();
70
71 if !opts.quiet {
72 let log_level = match opts.verbose {
73 0 => "warn",
74 1 => "info",
75 2 => "debug",
76 _ => "trace",
77 };
78
79 Logger::try_with_env_or_str(log_level)?
80 .format(flexi_logger::colored_default_format)
81 .set_palette("196;208;32;198;15".to_string())
82 .start()?;
83 }
84
85 let archive_path = match opts.depot {
86 Some(path) => path,
87 None => utils::find_archive_path()?,
88 };
89 let install_path = match opts.install_path {
90 Some(path) => path,
91 None => utils::home_directory()?,
92 };
93 let working_path = std::env::current_dir().expect("Failed to obtain current working directory");
94 log::debug!("Archive path : {}", archive_path.display());
95
96 let config = Config {
97 archive_path,
98 install_path,
99 working_path,
100 };
101
102 match opts.subcmd {
103 SubCommand::Init(opts) => commands::init::main(config, opts),
104 SubCommand::Link(opts) => commands::link::main(config, opts),
105 SubCommand::Mv(opts) => commands::mv::main(config, opts),
106 SubCommand::Status(opts) => commands::status::main(config, opts),
107 SubCommand::Unlink(opts) => commands::unlink::main(config, opts),
108 SubCommand::Install(opts) => commands::install::main(config, opts),
109 SubCommand::Uninstall(opts) => commands::uninstall::main(config, opts),
110 }
111}
diff --git a/dotup_cli/src/utils.rs b/dotup_cli/src/utils.rs
deleted file mode 100644
index b9a76a7..0000000
--- a/dotup_cli/src/utils.rs
+++ /dev/null
@@ -1,182 +0,0 @@
1use std::{
2 collections::VecDeque,
3 path::{Path, PathBuf},
4};
5
6use crate::prelude::*;
7
8const DEFAULT_DEPOT_NAME: &str = "depot.toml";
9
10pub fn home_directory() -> anyhow::Result<PathBuf> {
11 match std::env::var("HOME") {
12 Ok(val) => Ok(PathBuf::from(val)),
13 Err(e) => {
14 log::error!("Failed to get home directory from enviornment variable");
15 Err(e.into())
16 }
17 }
18}
19
20pub fn find_archive_path() -> anyhow::Result<PathBuf> {
21 let cwd = std::env::current_dir()?;
22 let compn = cwd.components().count();
23 let mut start = PathBuf::new();
24 for _ in 0..=compn {
25 start.push(DEFAULT_DEPOT_NAME);
26 if dotup::archive_exists(&start) {
27 return Ok(start);
28 }
29 start.pop();
30 start.push("..");
31 }
32 Ok(PathBuf::from(DEFAULT_DEPOT_NAME))
33}
34
35pub fn write_archive(path: impl AsRef<Path>, archive: &Archive) -> anyhow::Result<()> {
36 let path = path.as_ref();
37 log::debug!("Writing archive to {}", path.display());
38 match dotup::archive_write(path, archive) {
39 Ok(_) => Ok(()),
40 Err(e) => {
41 log::error!(
42 "Failed to write archive to : {}\nError : {}",
43 path.display(),
44 e
45 );
46 Err(e.into())
47 }
48 }
49}
50
51pub fn write_depot(depot: &Depot) -> anyhow::Result<()> {
52 let write_path = depot.archive_path();
53 let archive = depot.archive();
54 match dotup::archive_write(write_path, &archive) {
55 Ok(_) => Ok(()),
56 Err(e) => {
57 log::error!(
58 "Failed to write depot archive to : {}\nError : {}",
59 write_path.display(),
60 e
61 );
62 Err(e.into())
63 }
64 }
65}
66
67pub fn read_archive(path: impl AsRef<Path>) -> anyhow::Result<Archive> {
68 let path = path.as_ref();
69 match dotup::archive_read(path) {
70 Ok(archive) => Ok(archive),
71 Err(e) => {
72 log::error!(
73 "Failed to read archive from : {}\nError : {}",
74 path.display(),
75 e
76 );
77 Err(e.into())
78 }
79 }
80}
81
82pub fn read_depot(archive_path: impl AsRef<Path>) -> anyhow::Result<Depot> {
83 let archive_path = archive_path.as_ref().to_path_buf();
84 let archive = read_archive(&archive_path)?;
85 let depot_config = DepotConfig {
86 archive: Default::default(),
87 archive_path,
88 };
89 let mut depot = Depot::new(depot_config)?;
90
91 for archive_link in archive.links {
92 let link_params = LinkCreateParams::from(archive_link);
93 if let Err(e) = depot.create_link(link_params) {
94 log::warn!("Error while adding link : {}", e);
95 }
96 }
97
98 Ok(depot)
99}
100
101pub fn collect_links_by_base_paths(
102 depot: &Depot,
103 paths: impl IntoIterator<Item = impl AsRef<Path>>,
104) -> Vec<&Link> {
105 let canonical_paths: Vec<_> = paths
106 .into_iter()
107 .map(|p| p.as_ref().canonicalize().unwrap())
108 .collect();
109
110 depot
111 .links()
112 .filter(|&l| {
113 canonical_paths
114 .iter()
115 .any(|p| l.origin_canonical().starts_with(p))
116 })
117 .collect()
118}
119
120pub fn collect_link_ids_by_base_paths(
121 depot: &Depot,
122 paths: impl IntoIterator<Item = impl AsRef<Path>>,
123) -> Vec<LinkID> {
124 collect_links_by_base_paths(depot, paths)
125 .into_iter()
126 .map(|l| l.id())
127 .collect()
128}
129
130/// Returns a list of canonical paths to all the files in `dir`. This includes files in
131/// subdirectories.
132/// Fails if dir isnt a directory or if there is some other io error.
133pub fn collect_files_in_dir(dir: impl Into<PathBuf>) -> anyhow::Result<Vec<PathBuf>> {
134 let mut paths = Vec::new();
135 let mut dirs = VecDeque::new();
136 dirs.push_back(dir.into());
137
138 while let Some(dir) = dirs.pop_front() {
139 for entry in std::fs::read_dir(dir)? {
140 let entry = entry?;
141 let filetype = entry.file_type()?;
142 if filetype.is_dir() {
143 dirs.push_back(entry.path());
144 } else {
145 paths.push(entry.path());
146 }
147 }
148 }
149
150 Ok(paths)
151}
152
153/// Collects the result of std::fs::read_dir into two vecs
154/// The first one contains all the directories and the second one all the files
155pub fn collect_read_dir_split(
156 dir: impl AsRef<Path>,
157) -> anyhow::Result<(Vec<PathBuf>, Vec<PathBuf>)> {
158 Ok(std::fs::read_dir(dir)?
159 .filter_map(|e| e.ok())
160 .map(|e| e.path())
161 .partition(|p| p.is_dir()))
162}
163
164/// Checks if `path` is inside a git repository
165pub fn path_is_in_git_repo(path: &Path) -> bool {
166 let mut path = if !path.is_absolute() {
167 dbg!(dotup::utils::weakly_canonical(path))
168 } else {
169 path.to_owned()
170 };
171 let recurse = path.pop();
172 path.push(".git");
173 if path.is_dir() {
174 return true;
175 }
176 if recurse {
177 path.pop();
178 return path_is_in_git_repo(&path);
179 } else {
180 return false;
181 }
182}
diff --git a/dotup_cli/tests/cli.rs b/dotup_cli/tests/cli.rs
deleted file mode 100644
index c836f63..0000000
--- a/dotup_cli/tests/cli.rs
+++ /dev/null
@@ -1,145 +0,0 @@
1use assert_cmd::{assert::Assert, prelude::*};
2use dotup::ArchiveLink;
3use std::{
4 path::{Path, PathBuf},
5 process::Command,
6};
7use tempfile::TempDir;
8
9const DEPOT_FILE_NAME: &str = "depot.toml";
10const BIN_NAME: &str = "dotup";
11
12fn create_empty_file(path: impl AsRef<Path>) {
13 let path = path.as_ref();
14 if let Some(parent) = path.parent() {
15 std::fs::create_dir_all(parent).unwrap();
16 }
17 std::fs::write(path, "").unwrap();
18}
19
20fn prepare_command(dir: &TempDir) -> Command {
21 let mut cmd = Command::cargo_bin(BIN_NAME).unwrap();
22 cmd.current_dir(dir.path());
23 cmd
24}
25
26fn run_command(dir: &TempDir, cmd: &str) -> Assert {
27 let mut c = prepare_command(dir);
28 c.current_dir(dir.path());
29 c.args(cmd.split_whitespace());
30 c.assert()
31}
32
33fn prepare_dir() -> TempDir {
34 let dir = TempDir::new().unwrap();
35 create_empty_file(dir.path().join("o1/file.txt"));
36 create_empty_file(dir.path().join("o1/dir/file.txt"));
37 create_empty_file(dir.path().join("o2/file1.txt"));
38 create_empty_file(dir.path().join("o2/file2.txt"));
39 dir
40}
41
42#[test]
43fn test_cli_init() {
44 let dir = prepare_dir();
45 let assert = run_command(&dir, "init");
46
47 assert.success().code(0);
48 assert!(dir.path().join(DEPOT_FILE_NAME).is_file());
49}
50
51#[test]
52fn test_cli_link() {
53 let dir = prepare_dir();
54 run_command(&dir, "init").success();
55
56 let assert = run_command(&dir, "link o1 .config");
57 assert.success().code(0);
58
59 let assert = run_command(&dir, "link --directory o2 .scripts");
60 assert.success().code(0);
61
62 let archive = dotup::archive_read(dir.path().join(DEPOT_FILE_NAME)).unwrap();
63 let link1 = ArchiveLink {
64 origin: PathBuf::from("o1/file.txt"),
65 destination: PathBuf::from(".config/file.txt"),
66 };
67 let link2 = ArchiveLink {
68 origin: PathBuf::from("o1/dir/file.txt"),
69 destination: PathBuf::from(".config/dir/file.txt"),
70 };
71 let link3 = ArchiveLink {
72 origin: PathBuf::from("o2"),
73 destination: PathBuf::from(".scripts"),
74 };
75
76 assert!(archive.links.contains(&link1));
77 assert!(archive.links.contains(&link2));
78 assert!(archive.links.contains(&link3));
79}
80
81#[test]
82fn test_cli_install_uninstall_unlink() {
83 let dir = prepare_dir();
84 run_command(&dir, "init").success();
85 run_command(&dir, "link o1 .config").success();
86 run_command(&dir, "link --directory o2 .scripts").success();
87
88 let install_dir = TempDir::new().unwrap();
89 let install_base = format!("{}", install_dir.path().display());
90 run_command(
91 &dir,
92 &format!("--install-path {} install o1 o2", install_base),
93 )
94 .success();
95
96 assert_eq!(
97 std::fs::read_link(install_dir.path().join(".config/file.txt")).unwrap(),
98 dir.path().join("o1/file.txt")
99 );
100 assert_eq!(
101 std::fs::read_link(install_dir.path().join(".config/dir/file.txt")).unwrap(),
102 dir.path().join("o1/dir/file.txt")
103 );
104 assert_eq!(
105 std::fs::read_link(install_dir.path().join(".scripts")).unwrap(),
106 dir.path().join("o2")
107 );
108
109 run_command(
110 &dir,
111 &format!("--install-path {} uninstall o1/file.txt", install_base),
112 )
113 .success();
114 assert!(!install_dir.path().join(".config/file.txt").exists());
115 assert!(install_dir.path().join(".config/dir/file.txt").exists());
116 assert!(install_dir.path().join(".scripts").exists());
117
118 run_command(
119 &dir,
120 &format!("--install-path {} uninstall o1", install_base),
121 )
122 .success();
123 assert!(!install_dir.path().join(".config/file.txt").exists());
124 assert!(!install_dir.path().join(".config/dir/file.txt").exists());
125 assert!(install_dir.path().join(".scripts").exists());
126
127 assert_eq!(
128 3,
129 dotup::archive_read(dir.path().join(DEPOT_FILE_NAME))
130 .unwrap()
131 .links
132 .len()
133 );
134
135 run_command(&dir, &format!("unlink --uninstall {} o2", install_base)).success();
136 assert!(!install_dir.path().join(".scripts").exists());
137
138 assert_eq!(
139 2,
140 dotup::archive_read(dir.path().join(DEPOT_FILE_NAME))
141 .unwrap()
142 .links
143 .len()
144 );
145}