summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Cargo.lock149
-rw-r--r--Cargo.toml4
-rw-r--r--planmodlist.xoppbin342232 -> 342159 bytes
-rw-r--r--src/apis/modrinth.rs6
-rw-r--r--src/commands/io.rs20
-rw-r--r--src/commands/list.rs1
-rw-r--r--src/commands/mod.rs4
-rw-r--r--src/commands/modification.rs73
-rw-r--r--src/commands/setup.rs86
-rw-r--r--src/commands/update.rs90
-rw-r--r--src/db.rs101
-rw-r--r--src/error.rs2
-rw-r--r--src/files.rs10
-rw-r--r--src/input.rs19
-rw-r--r--src/main.rs14
-rw-r--r--tests/db.rs249
17 files changed, 321 insertions, 511 deletions
diff --git a/.gitignore b/.gitignore
index 8713145..390e3b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,11 @@
3/dl 3/dl
4/dl2 4/dl2
5/test_tmp 5/test_tmp
6/downloads
6.planmodlist.autosave.xopp 7.planmodlist.autosave.xopp
7data.db.cp 8data.db.cp
8export.toml 9export.toml
9config.toml 10config.toml
10/dev 11/dev
11/.fleet \ No newline at end of file 12/.fleet
13/.vscode \ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index cf7495b..e1b7443 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -60,9 +60,9 @@ dependencies = [
60 60
61[[package]] 61[[package]]
62name = "base64" 62name = "base64"
63version = "0.13.1" 63version = "0.21.0"
64source = "registry+https://github.com/rust-lang/crates.io-index" 64source = "registry+https://github.com/rust-lang/crates.io-index"
65checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 65checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
66 66
67[[package]] 67[[package]]
68name = "bitflags" 68name = "bitflags"
@@ -72,9 +72,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
72 72
73[[package]] 73[[package]]
74name = "bumpalo" 74name = "bumpalo"
75version = "3.11.1" 75version = "3.12.0"
76source = "registry+https://github.com/rust-lang/crates.io-index" 76source = "registry+https://github.com/rust-lang/crates.io-index"
77checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" 77checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
78 78
79[[package]] 79[[package]]
80name = "bytes" 80name = "bytes"
@@ -84,9 +84,9 @@ checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
84 84
85[[package]] 85[[package]]
86name = "cc" 86name = "cc"
87version = "1.0.78" 87version = "1.0.79"
88source = "registry+https://github.com/rust-lang/crates.io-index" 88source = "registry+https://github.com/rust-lang/crates.io-index"
89checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" 89checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
90 90
91[[package]] 91[[package]]
92name = "cfg-if" 92name = "cfg-if"
@@ -137,9 +137,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
137 137
138[[package]] 138[[package]]
139name = "cxx" 139name = "cxx"
140version = "1.0.86" 140version = "1.0.88"
141source = "registry+https://github.com/rust-lang/crates.io-index" 141source = "registry+https://github.com/rust-lang/crates.io-index"
142checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" 142checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
143dependencies = [ 143dependencies = [
144 "cc", 144 "cc",
145 "cxxbridge-flags", 145 "cxxbridge-flags",
@@ -149,9 +149,9 @@ dependencies = [
149 149
150[[package]] 150[[package]]
151name = "cxx-build" 151name = "cxx-build"
152version = "1.0.86" 152version = "1.0.88"
153source = "registry+https://github.com/rust-lang/crates.io-index" 153source = "registry+https://github.com/rust-lang/crates.io-index"
154checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" 154checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
155dependencies = [ 155dependencies = [
156 "cc", 156 "cc",
157 "codespan-reporting", 157 "codespan-reporting",
@@ -164,15 +164,15 @@ dependencies = [
164 164
165[[package]] 165[[package]]
166name = "cxxbridge-flags" 166name = "cxxbridge-flags"
167version = "1.0.86" 167version = "1.0.88"
168source = "registry+https://github.com/rust-lang/crates.io-index" 168source = "registry+https://github.com/rust-lang/crates.io-index"
169checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" 169checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
170 170
171[[package]] 171[[package]]
172name = "cxxbridge-macro" 172name = "cxxbridge-macro"
173version = "1.0.86" 173version = "1.0.88"
174source = "registry+https://github.com/rust-lang/crates.io-index" 174source = "registry+https://github.com/rust-lang/crates.io-index"
175checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" 175checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
176dependencies = [ 176dependencies = [
177 "proc-macro2", 177 "proc-macro2",
178 "quote", 178 "quote",
@@ -271,24 +271,30 @@ dependencies = [
271 271
272[[package]] 272[[package]]
273name = "futures-channel" 273name = "futures-channel"
274version = "0.3.25" 274version = "0.3.26"
275source = "registry+https://github.com/rust-lang/crates.io-index" 275source = "registry+https://github.com/rust-lang/crates.io-index"
276checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" 276checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
277dependencies = [ 277dependencies = [
278 "futures-core", 278 "futures-core",
279] 279]
280 280
281[[package]] 281[[package]]
282name = "futures-core" 282name = "futures-core"
283version = "0.3.25" 283version = "0.3.26"
284source = "registry+https://github.com/rust-lang/crates.io-index"
285checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
286
287[[package]]
288name = "futures-io"
289version = "0.3.26"
284source = "registry+https://github.com/rust-lang/crates.io-index" 290source = "registry+https://github.com/rust-lang/crates.io-index"
285checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 291checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
286 292
287[[package]] 293[[package]]
288name = "futures-macro" 294name = "futures-macro"
289version = "0.3.25" 295version = "0.3.26"
290source = "registry+https://github.com/rust-lang/crates.io-index" 296source = "registry+https://github.com/rust-lang/crates.io-index"
291checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" 297checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
292dependencies = [ 298dependencies = [
293 "proc-macro2", 299 "proc-macro2",
294 "quote", 300 "quote",
@@ -297,25 +303,28 @@ dependencies = [
297 303
298[[package]] 304[[package]]
299name = "futures-sink" 305name = "futures-sink"
300version = "0.3.25" 306version = "0.3.26"
301source = "registry+https://github.com/rust-lang/crates.io-index" 307source = "registry+https://github.com/rust-lang/crates.io-index"
302checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" 308checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
303 309
304[[package]] 310[[package]]
305name = "futures-task" 311name = "futures-task"
306version = "0.3.25" 312version = "0.3.26"
307source = "registry+https://github.com/rust-lang/crates.io-index" 313source = "registry+https://github.com/rust-lang/crates.io-index"
308checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" 314checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
309 315
310[[package]] 316[[package]]
311name = "futures-util" 317name = "futures-util"
312version = "0.3.25" 318version = "0.3.26"
313source = "registry+https://github.com/rust-lang/crates.io-index" 319source = "registry+https://github.com/rust-lang/crates.io-index"
314checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" 320checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
315dependencies = [ 321dependencies = [
316 "futures-core", 322 "futures-core",
323 "futures-io",
317 "futures-macro", 324 "futures-macro",
325 "futures-sink",
318 "futures-task", 326 "futures-task",
327 "memchr",
319 "pin-project-lite", 328 "pin-project-lite",
320 "pin-utils", 329 "pin-utils",
321 "slab", 330 "slab",
@@ -334,9 +343,9 @@ dependencies = [
334 343
335[[package]] 344[[package]]
336name = "gimli" 345name = "gimli"
337version = "0.27.0" 346version = "0.27.1"
338source = "registry+https://github.com/rust-lang/crates.io-index" 347source = "registry+https://github.com/rust-lang/crates.io-index"
339checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" 348checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec"
340 349
341[[package]] 350[[package]]
342name = "h2" 351name = "h2"
@@ -615,7 +624,7 @@ dependencies = [
615 624
616[[package]] 625[[package]]
617name = "modlist" 626name = "modlist"
618version = "0.9.0" 627version = "0.10.0"
619dependencies = [ 628dependencies = [
620 "chrono", 629 "chrono",
621 "dirs", 630 "dirs",
@@ -678,9 +687,9 @@ dependencies = [
678 687
679[[package]] 688[[package]]
680name = "object" 689name = "object"
681version = "0.30.2" 690version = "0.30.3"
682source = "registry+https://github.com/rust-lang/crates.io-index" 691source = "registry+https://github.com/rust-lang/crates.io-index"
683checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" 692checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
684dependencies = [ 693dependencies = [
685 "memchr", 694 "memchr",
686] 695]
@@ -748,9 +757,9 @@ dependencies = [
748 757
749[[package]] 758[[package]]
750name = "parking_lot_core" 759name = "parking_lot_core"
751version = "0.9.5" 760version = "0.9.6"
752source = "registry+https://github.com/rust-lang/crates.io-index" 761source = "registry+https://github.com/rust-lang/crates.io-index"
753checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" 762checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
754dependencies = [ 763dependencies = [
755 "cfg-if", 764 "cfg-if",
756 "libc", 765 "libc",
@@ -785,9 +794,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
785 794
786[[package]] 795[[package]]
787name = "proc-macro2" 796name = "proc-macro2"
788version = "1.0.49" 797version = "1.0.50"
789source = "registry+https://github.com/rust-lang/crates.io-index" 798source = "registry+https://github.com/rust-lang/crates.io-index"
790checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" 799checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
791dependencies = [ 800dependencies = [
792 "unicode-ident", 801 "unicode-ident",
793] 802]
@@ -832,9 +841,9 @@ dependencies = [
832 841
833[[package]] 842[[package]]
834name = "reqwest" 843name = "reqwest"
835version = "0.11.13" 844version = "0.11.14"
836source = "registry+https://github.com/rust-lang/crates.io-index" 845source = "registry+https://github.com/rust-lang/crates.io-index"
837checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" 846checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
838dependencies = [ 847dependencies = [
839 "base64", 848 "base64",
840 "bytes", 849 "bytes",
@@ -864,6 +873,7 @@ dependencies = [
864 "url", 873 "url",
865 "wasm-bindgen", 874 "wasm-bindgen",
866 "wasm-bindgen-futures", 875 "wasm-bindgen-futures",
876 "wasm-streams",
867 "web-sys", 877 "web-sys",
868 "winreg", 878 "winreg",
869] 879]
@@ -917,9 +927,9 @@ checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
917 927
918[[package]] 928[[package]]
919name = "security-framework" 929name = "security-framework"
920version = "2.7.0" 930version = "2.8.2"
921source = "registry+https://github.com/rust-lang/crates.io-index" 931source = "registry+https://github.com/rust-lang/crates.io-index"
922checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" 932checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
923dependencies = [ 933dependencies = [
924 "bitflags", 934 "bitflags",
925 "core-foundation", 935 "core-foundation",
@@ -930,9 +940,9 @@ dependencies = [
930 940
931[[package]] 941[[package]]
932name = "security-framework-sys" 942name = "security-framework-sys"
933version = "2.6.1" 943version = "2.8.0"
934source = "registry+https://github.com/rust-lang/crates.io-index" 944source = "registry+https://github.com/rust-lang/crates.io-index"
935checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 945checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
936dependencies = [ 946dependencies = [
937 "core-foundation-sys", 947 "core-foundation-sys",
938 "libc", 948 "libc",
@@ -1042,9 +1052,9 @@ dependencies = [
1042 1052
1043[[package]] 1053[[package]]
1044name = "termcolor" 1054name = "termcolor"
1045version = "1.1.3" 1055version = "1.2.0"
1046source = "registry+https://github.com/rust-lang/crates.io-index" 1056source = "registry+https://github.com/rust-lang/crates.io-index"
1047checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1057checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
1048dependencies = [ 1058dependencies = [
1049 "winapi-util", 1059 "winapi-util",
1050] 1060]
@@ -1097,9 +1107,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
1097 1107
1098[[package]] 1108[[package]]
1099name = "tokio" 1109name = "tokio"
1100version = "1.24.1" 1110version = "1.25.0"
1101source = "registry+https://github.com/rust-lang/crates.io-index" 1111source = "registry+https://github.com/rust-lang/crates.io-index"
1102checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" 1112checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
1103dependencies = [ 1113dependencies = [
1104 "autocfg", 1114 "autocfg",
1105 "bytes", 1115 "bytes",
@@ -1152,9 +1162,9 @@ dependencies = [
1152 1162
1153[[package]] 1163[[package]]
1154name = "toml" 1164name = "toml"
1155version = "0.5.10" 1165version = "0.5.11"
1156source = "registry+https://github.com/rust-lang/crates.io-index" 1166source = "registry+https://github.com/rust-lang/crates.io-index"
1157checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" 1167checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
1158dependencies = [ 1168dependencies = [
1159 "serde", 1169 "serde",
1160] 1170]
@@ -1193,9 +1203,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
1193 1203
1194[[package]] 1204[[package]]
1195name = "unicode-bidi" 1205name = "unicode-bidi"
1196version = "0.3.8" 1206version = "0.3.10"
1197source = "registry+https://github.com/rust-lang/crates.io-index" 1207source = "registry+https://github.com/rust-lang/crates.io-index"
1198checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 1208checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
1199 1209
1200[[package]] 1210[[package]]
1201name = "unicode-ident" 1211name = "unicode-ident"
@@ -1330,6 +1340,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1330checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 1340checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
1331 1341
1332[[package]] 1342[[package]]
1343name = "wasm-streams"
1344version = "0.2.3"
1345source = "registry+https://github.com/rust-lang/crates.io-index"
1346checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078"
1347dependencies = [
1348 "futures-util",
1349 "js-sys",
1350 "wasm-bindgen",
1351 "wasm-bindgen-futures",
1352 "web-sys",
1353]
1354
1355[[package]]
1333name = "web-sys" 1356name = "web-sys"
1334version = "0.3.60" 1357version = "0.3.60"
1335source = "registry+https://github.com/rust-lang/crates.io-index" 1358source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1387,45 +1410,45 @@ dependencies = [
1387 1410
1388[[package]] 1411[[package]]
1389name = "windows_aarch64_gnullvm" 1412name = "windows_aarch64_gnullvm"
1390version = "0.42.0" 1413version = "0.42.1"
1391source = "registry+https://github.com/rust-lang/crates.io-index" 1414source = "registry+https://github.com/rust-lang/crates.io-index"
1392checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1415checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
1393 1416
1394[[package]] 1417[[package]]
1395name = "windows_aarch64_msvc" 1418name = "windows_aarch64_msvc"
1396version = "0.42.0" 1419version = "0.42.1"
1397source = "registry+https://github.com/rust-lang/crates.io-index" 1420source = "registry+https://github.com/rust-lang/crates.io-index"
1398checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1421checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
1399 1422
1400[[package]] 1423[[package]]
1401name = "windows_i686_gnu" 1424name = "windows_i686_gnu"
1402version = "0.42.0" 1425version = "0.42.1"
1403source = "registry+https://github.com/rust-lang/crates.io-index" 1426source = "registry+https://github.com/rust-lang/crates.io-index"
1404checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1427checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
1405 1428
1406[[package]] 1429[[package]]
1407name = "windows_i686_msvc" 1430name = "windows_i686_msvc"
1408version = "0.42.0" 1431version = "0.42.1"
1409source = "registry+https://github.com/rust-lang/crates.io-index" 1432source = "registry+https://github.com/rust-lang/crates.io-index"
1410checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1433checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
1411 1434
1412[[package]] 1435[[package]]
1413name = "windows_x86_64_gnu" 1436name = "windows_x86_64_gnu"
1414version = "0.42.0" 1437version = "0.42.1"
1415source = "registry+https://github.com/rust-lang/crates.io-index" 1438source = "registry+https://github.com/rust-lang/crates.io-index"
1416checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1439checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
1417 1440
1418[[package]] 1441[[package]]
1419name = "windows_x86_64_gnullvm" 1442name = "windows_x86_64_gnullvm"
1420version = "0.42.0" 1443version = "0.42.1"
1421source = "registry+https://github.com/rust-lang/crates.io-index" 1444source = "registry+https://github.com/rust-lang/crates.io-index"
1422checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1445checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
1423 1446
1424[[package]] 1447[[package]]
1425name = "windows_x86_64_msvc" 1448name = "windows_x86_64_msvc"
1426version = "0.42.0" 1449version = "0.42.1"
1427source = "registry+https://github.com/rust-lang/crates.io-index" 1450source = "registry+https://github.com/rust-lang/crates.io-index"
1428checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1451checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
1429 1452
1430[[package]] 1453[[package]]
1431name = "winreg" 1454name = "winreg"
diff --git a/Cargo.toml b/Cargo.toml
index da48762..0b6ffa4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "modlist" 2name = "modlist"
3version = "0.9.0" 3version = "0.10.0"
4edition = "2021" 4edition = "2021"
5 5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -15,4 +15,4 @@ futures-util = "0.3.14"
15chrono = "0.4.22" 15chrono = "0.4.22"
16toml = "0.5.10" 16toml = "0.5.10"
17error-chain = "0.12.4" 17error-chain = "0.12.4"
18dirs = "4.0.0" \ No newline at end of file 18dirs = "4.0.0"
diff --git a/planmodlist.xopp b/planmodlist.xopp
index b3a54d3..a73d324 100644
--- a/planmodlist.xopp
+++ b/planmodlist.xopp
Binary files differ
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index 36ab5df..f3f89a7 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -63,9 +63,7 @@ pub enum Type {
63pub enum Status { 63pub enum Status {
64 approved, 64 approved,
65 rejected, 65 rejected,
66 draft, 66 draft, unlisted, archived,
67 unlisted,
68 archived,
69 processing, 67 processing,
70 unknown 68 unknown
71} 69}
@@ -141,7 +139,6 @@ pub async fn project(api: String, name: &str) -> Project {
141} 139}
142 140
143pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> { 141pub async fn projects(api: String, ids: Vec<String>) -> Vec<Project> {
144 println!("\tGet versions from modrinth\n");
145 let all = ids.join(r#"",""#); 142 let all = ids.join(r#"",""#);
146 let url = format!(r#"projects?ids=["{}"]"#, all); 143 let url = format!(r#"projects?ids=["{}"]"#, all);
147 144
@@ -187,7 +184,6 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
187 } 184 }
188 times.sort_by_key(|t| t.1); 185 times.sort_by_key(|t| t.1);
189 times.reverse(); 186 times.reverse();
190 println!("\t â””New current version: {}", times[0].0);
191 Ok(times[0].0.to_string()) 187 Ok(times[0].0.to_string())
192 }, 188 },
193 _ => panic!("available_versions should never be negative"), 189 _ => panic!("available_versions should never be negative"),
diff --git a/src/commands/io.rs b/src/commands/io.rs
index 4835e3d..44604d1 100644
--- a/src/commands/io.rs
+++ b/src/commands/io.rs
@@ -2,7 +2,7 @@ use std::fs::File;
2use std::io::prelude::*; 2use std::io::prelude::*;
3use serde::{Serialize, Deserialize}; 3use serde::{Serialize, Deserialize};
4 4
5use crate::{input::{Input, IoOptions}, db::{lists_get, userlist_get_all_ids, lists_get_all_ids, lists_insert}, config::Cfg, Modloader, /*mod_add,*/ List, devdir, error::MLE}; 5use crate::{input::{Input, IoOptions}, db::{lists_get, userlist_get_all_ids, lists_get_all_ids, lists_insert}, config::Cfg, Modloader, List, devdir, error::MLE, mods_add};
6 6
7#[derive(Debug, Serialize, Deserialize)] 7#[derive(Debug, Serialize, Deserialize)]
8struct Export { 8struct Export {
@@ -24,7 +24,7 @@ impl ExportList {
24 let list = lists_get(config.clone(), String::from(&list_id))?; 24 let list = lists_get(config.clone(), String::from(&list_id))?;
25 25
26 let mut dl_folder = None; 26 let mut dl_folder = None;
27 if download == true { dl_folder = Some(list.download_folder) }; 27 if download{ dl_folder = Some(list.download_folder) };
28 28
29 let mods = userlist_get_all_ids(config, list_id)?.join("|"); 29 let mods = userlist_get_all_ids(config, list_id)?.join("|");
30 30
@@ -51,7 +51,7 @@ fn export(config: Cfg, input: Input) -> MLE<()> {
51 } 51 }
52 let mut lists: Vec<ExportList> = vec![]; 52 let mut lists: Vec<ExportList> = vec![];
53 for list_id in list_ids { 53 for list_id in list_ids {
54 lists.push(ExportList::from(config.clone(), String::from(list_id), true)?); 54 lists.push(ExportList::from(config.clone(), list_id, true)?);
55 } 55 }
56 56
57 let toml = toml::to_string( &Export { lists } )?; 57 let toml = toml::to_string( &Export { lists } )?;
@@ -59,7 +59,7 @@ fn export(config: Cfg, input: Input) -> MLE<()> {
59 let filestr = dirs::home_dir().unwrap().join("mlexport.toml"); 59 let filestr = dirs::home_dir().unwrap().join("mlexport.toml");
60 60
61 let mut file = File::create(devdir(filestr.into_os_string().into_string().unwrap().as_str()))?; 61 let mut file = File::create(devdir(filestr.into_os_string().into_string().unwrap().as_str()))?;
62 file.write_all(&toml.as_bytes())?; 62 file.write_all(toml.as_bytes())?;
63 63
64 Ok(()) 64 Ok(())
65} 65}
@@ -67,8 +67,8 @@ fn export(config: Cfg, input: Input) -> MLE<()> {
67async fn import(config: Cfg, input: Input) -> MLE<()> { 67async fn import(config: Cfg, input: Input) -> MLE<()> {
68 68
69 let filestr: String = match input.file { 69 let filestr: String = match input.file {
70 Some(args) => String::from(args), 70 Some(args) => args,
71 None => String::from(devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str())), 71 None => devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str()),
72 }; 72 };
73 73
74 let mut file = File::open(filestr)?; 74 let mut file = File::open(filestr)?;
@@ -76,17 +76,17 @@ async fn import(config: Cfg, input: Input) -> MLE<()> {
76 file.read_to_string(&mut content)?; 76 file.read_to_string(&mut content)?;
77 let export: Export = toml::from_str(&content)?; 77 let export: Export = toml::from_str(&content)?;
78 78
79 println!("{:#?}", export);
80
81 for exportlist in export.lists { 79 for exportlist in export.lists {
82 let list = List { id: exportlist.id, mc_version: exportlist.mc_version, modloader: Modloader::from(&exportlist.launcher)?, download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap() }; 80 let list = List { id: exportlist.id, mc_version: exportlist.mc_version, modloader: Modloader::from(&exportlist.launcher)?, download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap() };
83 lists_insert(config.clone(), list.id.clone(), list.mc_version.clone(), list.modloader.clone(), String::from(&list.download_folder))?; 81 lists_insert(config.clone(), list.id.clone(), list.mc_version.clone(), list.modloader.clone(), String::from(&list.download_folder))?;
84 let mods: Vec<&str> = exportlist.mods.split("|").collect(); 82 let mods: Vec<&str> = exportlist.mods.split('|').collect();
85 let mut mod_ids = vec![]; 83 let mut mod_ids = vec![];
86 for mod_id in mods { 84 for mod_id in mods {
87 mod_ids.push(String::from(mod_id)); 85 mod_ids.push(String::from(mod_id));
88 }; 86 };
89 //mod_add(config.clone(), mod_ids, list.clone(), false).await?; 87 //TODO impl set_version and good direct download
88 //TODO impl all at once, dafuck
89 mods_add(config.clone(), mod_ids, list, input.direct_download, false).await?;
90 } 90 }
91 Ok(()) 91 Ok(())
92} 92}
diff --git a/src/commands/list.rs b/src/commands/list.rs
index eaf6fa1..8e86973 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -49,6 +49,7 @@ fn remove(config: Cfg, input: Input) -> MLE<()> {
49} 49}
50 50
51///Changing the current lists version and updating it 51///Changing the current lists version and updating it
52///
52/// #Arguments 53/// #Arguments
53/// 54///
54/// * `config` - The current config 55/// * `config` - The current config
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 38139f9..0d5bd00 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,13 +1,13 @@
1pub mod modification; 1pub mod modification;
2pub mod list; 2pub mod list;
3pub mod update; 3pub mod update;
4//pub mod setup; 4pub mod setup;
5pub mod download; 5pub mod download;
6pub mod io; 6pub mod io;
7 7
8pub use modification::*; 8pub use modification::*;
9pub use list::*; 9pub use list::*;
10pub use update::*; 10pub use update::*;
11//pub use setup::*; 11pub use setup::*;
12pub use download::*; 12pub use download::*;
13pub use io::*; 13pub use io::*;
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index c82d6b5..12a635f 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,4 +1,9 @@
1use crate::{modrinth::{project, versions, extract_current_version, Version, projects}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, mods_get_all_ids, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, input::{Input, ModOptions}, files::{delete_version, download_versions}, List, error::{MLE, ErrorType, MLError}}; 1use crate::{modrinth::{project, versions, extract_current_version, Version, projects}, config::Cfg, db::{mods_insert, userlist_remove, mods_get_id, userlist_insert, userlist_get_all_ids, userlist_get_current_version, lists_get_all_ids, mods_remove}, input::{Input, ModOptions}, files::{delete_version, download_versions}, List, error::{MLE, ErrorType, MLError}};
2
3//TODO DO IT
4pub struct ModVer {
5
6}
2 7
3pub async fn modification(config: Cfg, input: Input) -> MLE<()> { 8pub async fn modification(config: Cfg, input: Input) -> MLE<()> {
4 match input.clone().mod_options.ok_or("").unwrap() { 9 match input.clone().mod_options.ok_or("").unwrap() {
@@ -13,22 +18,26 @@ pub async fn modification(config: Cfg, input: Input) -> MLE<()> {
13 18
14async fn add(config: Cfg, input: Input) -> MLE<()> { 19async fn add(config: Cfg, input: Input) -> MLE<()> {
15 20
16 mod_add(config, vec![String::from(input.mod_id.unwrap())], input.list.unwrap(), input.direct_download).await?; 21 mods_add(config, vec![input.mod_id.unwrap()], input.list.unwrap(), input.direct_download, input.set_version).await?;
17 22
18 Ok(()) 23 Ok(())
19} 24}
20 25//TODO impl specific version
21pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, direct_download: bool) -> MLE<()> { 26pub async fn mods_add(config: Cfg, mod_id: Vec<String>, list: List, direct_download: bool, set_version: bool) -> MLE<()> {
22 27
23 //Fix printing (its horrible) 28 println!("Add mods to {}", list.id);
24 println!("Adding mod(s) {:?}", mod_id); 29 println!(" â””Add mods:");
25 let projects = if mod_id.len() == 1 { 30 let projects = if mod_id.len() == 1 {
26 vec![project(String::from(&config.apis.modrinth), &mod_id[0]).await] 31 vec![project(String::from(&config.apis.modrinth), &mod_id[0]).await]
27 } else { 32 } else {
28 projects(String::from(&config.apis.modrinth), mod_id).await 33 projects(String::from(&config.apis.modrinth), mod_id).await
29 }; 34 };
35
36 let mut downloadstack: Vec<Version> = Vec::new();
30 37
31 for project in projects { 38 for project in projects {
39 println!("\tâ””{}", project.title);
40 println!("\t â””Get versions");
32 let available_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await; 41 let available_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await;
33 42
34 let mut available_versions_vec: Vec<String> = Vec::new(); 43 let mut available_versions_vec: Vec<String> = Vec::new();
@@ -37,64 +46,56 @@ pub async fn mod_add(config: Cfg, mod_id: Vec<String>, list: List, direct_downlo
37 let file: String; 46 let file: String;
38 if !available_versions.is_empty() { 47 if !available_versions.is_empty() {
39 let current_id = extract_current_version(available_versions.clone())?; 48 let current_id = extract_current_version(available_versions.clone())?;
49 println!("\t â””Current version: {}", current_id);
40 50
41 current_version = Some(available_versions.clone().into_iter().find(|v| v.id == current_id).unwrap()); 51 current_version = Some(available_versions.clone().into_iter().find(|v| v.id == current_id).unwrap());
42 52
43 current_version_id = current_version.clone().unwrap().id; 53 current_version_id = current_version.clone().unwrap().id;
44 54
45 file = current_version.clone().ok_or("").unwrap().files.into_iter().find(|f| f.primary).unwrap().url; 55 file = current_version.clone().ok_or("").unwrap().files.into_iter().find(|f| f.primary).unwrap().url;
46
47 for ver in available_versions { 56 for ver in available_versions {
48 available_versions_vec.push(ver.id); 57 available_versions_vec.push(ver.id);
49 }; 58 };
50 } else { 59 } else {
51 println!("There's currently no mod version for your specified target"); 60 println!("\t â””There's currently no mod version for your specified target");
52 current_version = None; 61 current_version = None;
53 current_version_id = String::from("NONE"); 62 current_version_id = String::from("NONE");
54 file = String::from("NONE"); 63 file = String::from("NONE");
55 available_versions_vec.push(String::from("NONE")); 64 available_versions_vec.push(String::from("NONE"));
56 } 65 }
57 66
58 //add to current list and mod table 67 match userlist_insert(config.clone(), &list.id, &project.id, &current_version_id, available_versions_vec, &file, set_version) {
59 match userlist_get_all_ids(config.clone(), list.clone().id) { 68 Err(e) => {
60 Ok(mods) => { 69 let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id);
61 if mods.contains(&project.id) { 70 if e.to_string() == expected_err { Err(MLError::new(ErrorType::ModError, "MOD_ALREADY_ON_SELECTED_LIST")) } else { Err(e) }
62 return Err(MLError::new(ErrorType::ModError, "MOD_ALREADY_ON_LIST")); }
63 else {
64 userlist_insert(config.clone(), String::from(&list.id), String::from(&project.id), String::from(&current_version_id), available_versions_vec, file)?;
65 }
66 }, 71 },
67 Err(..) => userlist_insert(config.clone(), String::from(&list.id), String::from(&project.id), String::from(&current_version_id), available_versions_vec, file)?, 72 Ok(..) => { Ok(..) },
68 }; 73 }?;
69 74
70 match mods_get_all_ids(config.clone()) { 75 match mods_insert(config.clone(), &project.id, &project.slug, &project.title) {
71 Ok(mods) => { 76 Err(e) => {
72 if mods.contains(&project.id) { 77 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" { Ok(..) } else { Err(e) }
73 //return Err(Box::new(Error::new(ErrorKind::Other, "MOD_ALREADY_IN_DATABASE")))
74 } else {
75 mods_insert(config.clone(), String::from(&project.id), String::from(&project.title), project.versions)?;
76 }
77 },
78 Err(..) => {
79 mods_insert(config.clone(), String::from(&project.id), String::from(&project.title), project.versions)?;
80 }, 78 },
81 }; 79 Ok(..) => Ok(..),
80 }?;
82 81
83 if direct_download && current_version.is_some() { download_versions(list.clone(), config.clone(), vec![current_version.unwrap()]).await?; }; 82 downloadstack.push(current_version.unwrap());
84 83 };
85 } 84
85 //Download all the added mods
86 if direct_download {
87 download_versions(list.clone(), config.clone(), downloadstack).await?;
88 };
86 89
87 Ok(()) 90 Ok(())
88} 91}
89 92
90fn remove(config: Cfg, input: Input) -> MLE<()> { 93fn remove(config: Cfg, input: Input) -> MLE<()> {
91 94
92 //TODO inplement deletion by slug or title 95 let mod_id = mods_get_id(&config.data, input.mod_id.as_ref().unwrap())?;
93 let mod_id = mods_get_id(config.clone(), input.clone().mod_id.unwrap())?;
94 96
95 let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; 97 let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?;
96 98
97 //TODO implement remove from modlist if not in any other lists && config clean is true
98 userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; 99 userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?;
99 delete_version(input.list.unwrap(), version)?; 100 delete_version(input.list.unwrap(), version)?;
100 101
diff --git a/src/commands/setup.rs b/src/commands/setup.rs
index cc7472c..0161bd7 100644
--- a/src/commands/setup.rs
+++ b/src/commands/setup.rs
@@ -1,66 +1,72 @@
1use std::{fs::File, path::Path, io::{Error, ErrorKind}}; 1use std::{fs::File, path::Path};
2 2
3use crate::{config::Cfg, db::{db_setup, s_config_get_version, s_config_create_version, s_insert_column, lists_get_all_ids, lists_get, userlist_get_all_current_version_ids, s_userlist_update_download, s_config_update_version}, modrinth::get_raw_versions, devdir}; 3use crate::{config::Cfg, db::db_setup, error::MLE, devdir};
4 4
5pub async fn setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { 5pub async fn setup(config: Cfg) -> MLE<()> {
6 let db_file = devdir(format!("{}/data.db", config.data).as_str()); 6 let db_file = devdir(format!("{}/data.db", config.data).as_str());
7 7
8 if !Path::new(&db_file).exists() { 8 if !Path::new(&db_file).exists() {
9 return create(config, db_file); 9 create(config, db_file)?;
10 } 10 }
11 11
12 /*
12 match s_config_get_version(config.clone()) { 13 match s_config_get_version(config.clone()) {
13 Ok(ver) => { 14 Ok(ver) => {
14 match ver.as_str() { 15 match ver.as_str() {
15 "0.2" => to_03(config)?, 16 "0.2" => to_03(config)?,
16 "0.3" => to_04(config)?, 17 "0.3" => to_04(config)?,
17 _ => return Err(Box::new(Error::new(ErrorKind::Other, "UNKNOWN_VERSION"))) 18 _ => return Err(MLError::new(ErrorType::Other, "UNKNOWN_VERSION"))
18 } 19 }
19 }, 20 },
20 Err(..) => to_02(config).await? 21 Err(..) => to_02(config).await?
21 }; 22 };
23 */
22 24
23 Ok(()) 25 Ok(())
24} 26}
25 27
26fn create(config: Cfg, db_file: String) -> Result<(), Box<dyn std::error::Error>> { 28fn create(config: Cfg, db_file: String) -> MLE<()> {
27 File::create(db_file)?;
28 db_setup(config)?;
29 Ok(())
30}
31
32async fn to_02(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
33 let lists = lists_get_all_ids(config.clone())?;
34
35 for list in lists {
36 println!("Updating {}", list);
37 s_insert_column(config.clone(), String::from(&list), String::from("current_download"), String::from("TEXT"), None)?;
38
39 let full_list = lists_get(config.clone(), String::from(&list))?;
40 29
41 let versions = userlist_get_all_current_version_ids(config.clone(), full_list.clone().id)?; 30 println!("Create database");
42
43 let raw_versions = get_raw_versions(String::from(&config.apis.modrinth), versions).await;
44
45 for ver in raw_versions {
46 println!("Adding link for {}", ver.project_id);
47 let file = ver.files.into_iter().find(|f| f.primary).unwrap();
48 s_userlist_update_download(config.clone(), String::from(&full_list.id), ver.project_id, file.url)?;
49 }
50 };
51 s_config_create_version(config)?;
52 31
32 File::create(db_file)?;
33 db_setup(config)?;
53 Ok(()) 34 Ok(())
54} 35}
55 36
56fn to_03(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { 37//async fn to_02(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
57 s_insert_column(config.clone(), String::from("lists"), String::from("download_folder"), String::from("TEXT"), None)?; 38// let lists = lists_get_all_ids(config.clone())?;
58 s_config_update_version(config, String::from("0.3")) 39//
59} 40// for list in lists {
41// println!("Updating {}", list);
42// s_insert_column(config.clone(), String::from(&list), String::from("current_download"), String::from("TEXT"), None)?;
43//
44// let full_list = lists_get(config.clone(), String::from(&list))?;
45//
46// let versions = userlist_get_all_current_version_ids(config.clone(), full_list.clone().id)?;
47//
48// let raw_versions = get_raw_versions(String::from(&config.apis.modrinth), versions).await;
49//
50// for ver in raw_versions {
51// println!("Adding link for {}", ver.project_id);
52// let file = ver.files.into_iter().find(|f| f.primary).unwrap();
53// s_userlist_update_download(config.clone(), String::from(&full_list.id), ver.project_id, file.url)?;
54// }
55// };
56// s_config_create_version(config)?;
57//
58// Ok(())
59//}
60//
61//fn to_03(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
62// s_insert_column(config.clone(), String::from("lists"), String::from("download_folder"), String::from("TEXT"), None)?;
63// s_config_update_version(config, String::from("0.3"))
64//}
65//
66//fn to_04(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
67// for list_id in lists_get_all_ids(config.clone())? {
68// s_insert_column(config.clone(), list_id, String::from("disabled_versions"), String::from("TEXT"), Some(String::from("NONE")))?;
69// }
70// s_config_update_version(config, String::from("0.4"))
71//}
60 72
61fn to_04(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
62 for list_id in lists_get_all_ids(config.clone())? {
63 s_insert_column(config.clone(), list_id, String::from("disabled_versions"), String::from("TEXT"), Some(String::from("NONE")))?;
64 }
65 s_config_update_version(config, String::from("0.4"))
66} \ No newline at end of file
diff --git a/src/commands/update.rs b/src/commands/update.rs
index d400a24..bc5b316 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,4 +1,4 @@
1use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, mods_get_versions, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_change_versions}, List, input::Input, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}}; 1use crate::{config::Cfg, modrinth::{versions, extract_current_version, Version}, get_current_list, db::{userlist_get_all_ids, userlist_get_applicable_versions, userlist_change_versions, lists_get_all_ids, lists_get, userlist_get_current_version, mods_get_title, userlist_get_set_version}, List, input::Input, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}};
2 2
3pub async fn update(config: Cfg, input: Input) -> MLE<()> { 3pub async fn update(config: Cfg, input: Input) -> MLE<()> {
4 let mut liststack: Vec<List> = vec![]; 4 let mut liststack: Vec<List> = vec![];
@@ -9,7 +9,7 @@ pub async fn update(config: Cfg, input: Input) -> MLE<()> {
9 } 9 }
10 } else { 10 } else {
11 let current = get_current_list(config.clone())?; 11 let current = get_current_list(config.clone())?;
12 println!("Check for updates of mods in list {}", current.id); 12 println!("Update list {}:", current.id);
13 liststack.push(current) 13 liststack.push(current)
14 } 14 }
15 cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await 15 cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await
@@ -20,59 +20,41 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
20 let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?; 20 let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?;
21 21
22 let mut current_versions: Vec<(String, String)> = vec![]; 22 let mut current_versions: Vec<(String, String)> = vec![];
23
24 println!(" â””Update mods:");
25 let mut updatestack: Vec<Version> = vec![];
26
27 for id in mods {
28 let title = mods_get_title(config.clone(), &id)?;
29 println!("\tâ””{}", title);
23 30
24 let mut versions = mods_get_versions(config.clone(), mods.clone())?; 31 if userlist_get_set_version(config.clone(), &current_list.id, &id)? {
25 versions.sort_by_key(|ver| ver.mod_id.clone()); 32 println!("\t â””Set version, skipping update");
26 33 continue;
27 let mut projects = projects(String::from(&config.apis.modrinth), mods).await; 34 }
28 projects.sort_by_key(|pro| pro.id.clone());
29 35
30 println!("Comparing mod versions:");
31 let mut updatestack: Vec<Version> = vec![];
32 for (index, project) in projects.into_iter().enumerate() {
33 //Get versions for project and check if they match up
34 let current_version = &versions[index];
35 let p_id = String::from(&project.id);
36 let v_id = &current_version.mod_id;
37 if &p_id != v_id { return Err(MLError::new(ErrorType::Other, "SORTING_ERROR")) };
38
39 println!("\t({}) Check for update", project.title);
40
41 //Getting current installed version for disable or delete 36 //Getting current installed version for disable or delete
42 let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&project.id))?; 37 let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&id))?;
43
44 let version_db_string = project.versions.join("|");
45 38
46 //Adding to stack if not the same versions in the list OR if clean == true 39 updatestack.push(
47 if clean || (version_db_string != current_version.versions) { 40 match specific_update(config.clone(), clean, current_list.clone(), String::from(&id)).await {
48 updatestack.push(match specific_update(config.clone(), clean, current_list.clone(), project.clone()).await {
49 Ok(ver) => { 41 Ok(ver) => {
50 current_versions.push((disable_version, p_id)); 42 current_versions.push((disable_version, id));
51 ver 43 ver
52 }, 44 },
53 Err(e) => { 45 Err(e) => {
54 //Catch no update available
55 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { 46 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
56 mods_change_versions(config.clone(), version_db_string, project.id)?; 47 println!("\t â””No new version found for the specified minecraft version");
57 println!("\t â””No new version found for the specified minecraft version");
58 } else { 48 } else {
59 return Err(e); 49 return Err(e);
60 }; 50 };
61 continue; 51 continue;
62 }, 52 }
63 }); 53 }
64 } else { 54 )
65 println!("\t â””No new version found");
66 };
67 }; 55 };
68
69 //Linebreak readability
70 println!("");
71 56
72 if clean { clean_list_dir(&current_list)? }; 57 if clean { clean_list_dir(&current_list)?; };
73
74 //Linebreak readability
75 println!("");
76 58
77 if direct_download && !updatestack.is_empty() { 59 if direct_download && !updatestack.is_empty() {
78 download_versions(current_list.clone(), config.clone(), updatestack).await?; 60 download_versions(current_list.clone(), config.clone(), updatestack).await?;
@@ -81,10 +63,10 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
81 if !clean { 63 if !clean {
82 for ver in current_versions { 64 for ver in current_versions {
83 if delete_old { 65 if delete_old {
84 println!("Deleting version {} for mod {}", ver.0, ver.1); 66 println!("\t â””Delete version {}", ver.0);
85 delete_version(current_list.clone(), ver.0)?; 67 delete_version(current_list.clone(), ver.0)?;
86 } else if ver.0 != "NONE" { 68 } else if ver.0 != "NONE" {
87 println!("Disabling version {} for mod {}", ver.0, ver.1); 69 println!("\t â””Disable version {}", ver.0);
88 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?; 70 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
89 }; 71 };
90 } 72 }
@@ -95,8 +77,8 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
95 Ok(()) 77 Ok(())
96} 78}
97 79
98async fn specific_update(config: Cfg, clean: bool, list: List, project: Project) -> MLE<Version> { 80async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE<Version> {
99 let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await; 81 let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&id), list.clone()).await;
100 82
101 let mut versions: Vec<String> = vec![]; 83 let mut versions: Vec<String> = vec![];
102 84
@@ -108,12 +90,20 @@ async fn specific_update(config: Cfg, clean: bool, list: List, project: Project)
108 versions.push(String::from("NONE")); 90 versions.push(String::from("NONE"));
109 } 91 }
110 92
111
112 let mut current: Vec<Version> = vec![]; 93 let mut current: Vec<Version> = vec![];
113 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&project.id))?) { 94 //TODO Split clean and no match
114 //get new versions 95 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&id))?) {
115 println!("\t â””Get versions for specified minecraft versions"); 96
116 let current_str = extract_current_version(applicable_versions.clone())?; 97 let current_str = extract_current_version(applicable_versions.clone())?;
98
99 if clean {
100 println!("\t â””Add version to downloadstack");
101 } else {
102 println!("\t â””Get versions for specified minecraft versions");
103 println!("\t â””New current version: {}", current_str);
104 };
105
106 //get new versions
117 let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") { 107 let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") {
118 Ok(v) => Ok(v), 108 Ok(v) => Ok(v),
119 Err(e) => Err(MLError::new(ErrorType::Other, e)), 109 Err(e) => Err(MLError::new(ErrorType::Other, e)),
@@ -124,7 +114,7 @@ async fn specific_update(config: Cfg, clean: bool, list: List, project: Project)
124 Ok(p) => Ok(p), 114 Ok(p) => Ok(p),
125 Err(e) => Err(MLError::new(ErrorType::Other, e)), 115 Err(e) => Err(MLError::new(ErrorType::Other, e)),
126 }?.url; 116 }?.url;
127 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, project.id)?; 117 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?;
128 } 118 }
129 119
130 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) }; 120 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) };
@@ -139,7 +129,7 @@ async fn download_updates_test() {
139 use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, Modloader, List}; 129 use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, Modloader, List};
140 130
141 let config = Cfg::init("modlist.toml").unwrap(); 131 let config = Cfg::init("modlist.toml").unwrap();
142 let current_list = List { id: String::from("..."), mc_version: String::from("..."), modloader: Modloader::Forge, download_folder: String::from("./dl") }; 132 let current_list = List { id: String::from("..."), mc_version: String::from("..."), modloader: Modloader::Fabric, download_folder: String::from("./dev/tests/dl") };
143 133
144 let versions = vec![Version { 134 let versions = vec![Version {
145 id: "dEqtGnT9".to_string(), 135 id: "dEqtGnT9".to_string(),
diff --git a/src/db.rs b/src/db.rs
index f47bda6..ecc6854 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -4,17 +4,17 @@ use rusqlite::Connection;
4 4
5use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}}; 5use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}};
6 6
7//mods 7//MODS
8pub fn mods_insert(config: Cfg, id: String, name: String, versions: Vec<String>) -> MLE<()> { 8pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> {
9 9
10 println!("Inserting mod {}({}) into database", name, id); 10 println!("\t â””Save mod info");
11 11
12 let data = devdir(format!("{}/data.db", config.data).as_str()); 12 let data = devdir(format!("{}/data.db", config.data).as_str());
13 let connection = Connection::open(data)?; 13 let connection = Connection::open(data)?;
14 14
15 connection.execute( 15 connection.execute(
16 "INSERT INTO mods (id, name, versions) VALUES (?1, ?2, ?3)", 16 "INSERT INTO mods (id, slug, title) VALUES (?1, ?2, ?3)",
17 [id, name.replace('\'', ""), versions.join("|")] 17 [id, slug, name.replace('\'', "").as_str()]
18 )?; 18 )?;
19 19
20 Ok(()) 20 Ok(())
@@ -41,32 +41,53 @@ pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::
41 } 41 }
42} 42}
43 43
44pub fn mods_get_id(config: Cfg, name: String) -> MLE<String> { 44///Get mod id based on the slug or name
45 let data = devdir(format!("{}/data.db", config.data).as_str()); 45///# Arguments
46///
47///* `data` - file directory of the database
48///* `slug` - Slug or Name of a mod
49///
50///# Failure
51///
52///Will return `MLError` when no mod id is found
53pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> {
54 let data = devdir(format!("{}/data.db", data).as_str());
46 let connection = Connection::open(data)?; 55 let connection = Connection::open(data)?;
47 56
48 let mut mod_id = String::new(); 57 let mut mod_id = String::new();
49 let mut stmt = connection.prepare("SELECT id FROM mods WHERE name = ?")?; 58
50 let id_iter = stmt.query_map([name], |row| { 59 //get from slug
60 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?;
61 let id_iter = stmt.query_map([slug], |row| {
51 row.get::<usize, String>(0) 62 row.get::<usize, String>(0)
52 })?; 63 })?;
53 64
54 for id in id_iter { 65 for id in id_iter {
55 mod_id = id?; 66 mod_id = id?;
56 }; 67 };
57 68 //get from title if no id found from slug
58 match mod_id.is_empty() { 69 if mod_id.is_empty() {
59 true => Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND")), 70 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?;
60 false => Ok(mod_id), 71 let id_iter = stmt.query_map([slug], |row| {
72 row.get::<usize, String>(0)
73 })?;
74
75 for id in id_iter {
76 mod_id = id?;
77 };
61 } 78 }
79
80 if mod_id.is_empty() { return Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND")) };
81
82 Ok(mod_id)
62} 83}
63 84
64pub fn mods_get_name(config: Cfg, id: &str) -> MLE<String> { 85pub fn mods_get_title(config: Cfg, id: &str) -> MLE<String> {
65 let data = devdir(format!("{}/data.db", config.data).as_str()); 86 let data = devdir(format!("{}/data.db", config.data).as_str());
66 let connection = Connection::open(data)?; 87 let connection = Connection::open(data)?;
67 88
68 let mut mod_name = String::new(); 89 let mut mod_name = String::new();
69 let mut stmt = connection.prepare("SELECT name FROM mods WHERE id = ?")?; 90 let mut stmt = connection.prepare("SELECT title FROM mods WHERE id = ?")?;
70 let name_iter = stmt.query_map([id], |row| { 91 let name_iter = stmt.query_map([id], |row| {
71 row.get::<usize, String>(0) 92 row.get::<usize, String>(0)
72 })?; 93 })?;
@@ -81,17 +102,6 @@ pub fn mods_get_name(config: Cfg, id: &str) -> MLE<String> {
81 } 102 }
82} 103}
83 104
84pub fn mods_change_versions(config: Cfg, versions: String, mod_id: String) -> MLE<()> {
85
86 //println!("Updating versions for {} with \n {}", mod_id, versions);
87
88 let data = devdir(format!("{}/data.db", config.data).as_str());
89 let connection = Connection::open(data)?;
90
91 connection.execute("UPDATE mods SET versions = ?1 WHERE id = ?2", [versions, mod_id])?;
92 Ok(())
93}
94
95pub fn mods_remove(config: Cfg, id: String) -> MLE<()> { 105pub fn mods_remove(config: Cfg, id: String) -> MLE<()> {
96 106
97 println!("Removing mod {} from database", id); 107 println!("Removing mod {} from database", id);
@@ -124,7 +134,7 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
124 } 134 }
125 135
126 let mut versionmaps: Vec<DBModlistVersions> = Vec::new(); 136 let mut versionmaps: Vec<DBModlistVersions> = Vec::new();
127 let mut stmt = connection.prepare(format!("SELECT id, versions, name FROM mods {}", wherestr).as_str())?; 137 let mut stmt = connection.prepare(format!("SELECT id, versions, title FROM mods {}", wherestr).as_str())?;
128 let id_iter = stmt.query_map([], |row| { 138 let id_iter = stmt.query_map([], |row| {
129 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?]) 139 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?])
130 })?; 140 })?;
@@ -143,14 +153,18 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
143} 153}
144 154
145//userlist 155//userlist
146pub fn userlist_insert(config: Cfg, list_id: String, mod_id: String, current_version: String, applicable_versions: Vec<String>, current_link: String) -> MLE<()> { 156pub fn userlist_insert(config: Cfg, list_id: &str, mod_id: &str, current_version: &str, applicable_versions: Vec<String>, current_link: &str, set_version: bool) -> MLE<()> {
147 println!("Inserting {} into current list({})", mod_id, list_id); 157 println!("\t â””Insert in list");
148 158
149 let data = devdir(format!("{}/data.db", config.data).as_str()); 159 let data = devdir(format!("{}/data.db", config.data).as_str());
150 let connection = Connection::open(data)?; 160 let connection = Connection::open(data)?;
151
152 161
153 connection.execute(format!("INSERT INTO {} VALUES (?1, ?2, ?3, ?4, 'NONE')", list_id).as_str(), [mod_id, current_version, applicable_versions.join("|"), current_link])?; 162 let sv = match set_version {
163 true => "1",
164 false => "0",
165 };
166
167 connection.execute(format!("INSERT INTO {} VALUES (?1, ?2, ?3, ?4, 'NONE', ?5)", list_id).as_str(), [mod_id, current_version, applicable_versions.join("|").as_str(), current_link, sv])?;
154 168
155 Ok(()) 169 Ok(())
156} 170}
@@ -285,6 +299,23 @@ pub fn userlist_get_all_current_versions_with_mods(config: Cfg, list_id: String)
285 Ok(versions) 299 Ok(versions)
286} 300}
287 301
302pub fn userlist_get_set_version(config:Cfg, list_id: &str, mod_id: &str) -> MLE<bool> {
303 let data = devdir(format!("{}/data.db", config.data).as_str());
304 let connection = Connection::open(data).unwrap();
305
306 let mut set_version: bool = false;
307 let mut stmt = connection.prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
308 let ver_iter = stmt.query_map([&mod_id], |row| {
309 row.get::<usize, bool>(0)
310 })?;
311
312 for ver in ver_iter {
313 set_version = ver?;
314 };
315
316 Ok(set_version)
317}
318
288pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> MLE<()> { 319pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> MLE<()> {
289 let data = devdir(format!("{}/data.db", config.data).as_str()); 320 let data = devdir(format!("{}/data.db", config.data).as_str());
290 let connection = Connection::open(data)?; 321 let connection = Connection::open(data)?;
@@ -357,7 +388,7 @@ pub fn lists_insert(config: Cfg, id: String, mc_version: String, mod_loader: Mod
357 let connection = Connection::open(data)?; 388 let connection = Connection::open(data)?;
358 389
359 connection.execute("INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", [id.clone(), mc_version, mod_loader.to_string(), download_folder])?; 390 connection.execute("INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", [id.clone(), mc_version, mod_loader.to_string(), download_folder])?;
360 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE' )", id).as_str(), [])?; 391 connection.execute(format!("CREATE TABLE {}( 'mod_id' TEXT, 'current_version' TEXT, 'applicable_versions' BLOB, 'current_download' TEXT, 'disabled_versions' TEXT DEFAULT 'NONE', 'set_version' INTEGER, CONSTRAINT {}_PK PRIMARY KEY (mod_id) )", id, id).as_str(), [])?;
361 392
362 Ok(()) 393 Ok(())
363} 394}
@@ -505,7 +536,7 @@ pub fn s_insert_column(config: Cfg, table: String, column: String, c_type: Strin
505 Ok(()) 536 Ok(())
506} 537}
507 538
508pub fn db_setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { 539pub fn db_setup(config: Cfg) -> MLE<()> {
509 540
510 println!("Initiating database"); 541 println!("Initiating database");
511 542
@@ -514,9 +545,9 @@ pub fn db_setup(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
514 545
515 connection.execute_batch( 546 connection.execute_batch(
516 "CREATE TABLE 'user_config' ( 'id' TEXT, 'value' TEXT ); 547 "CREATE TABLE 'user_config' ( 'id' TEXT, 'value' TEXT );
517 CREATE TABLE 'mods' ( 'id' TEXT, 'name' TEXT, 'versions' TEXT ); 548 CREATE TABLE 'mods' ( 'id' TEXT, 'slug' TEXT, 'title' TEXT, CONSTRAINT mods_PK PRIMARY KEY (id) );
518 CREATE TABLE 'lists' ( 'id' TEXT, 'mc_version' TEXT, 'modloader' TEXT, 'download_folder' TEXT ); 549 CREATE TABLE 'lists' ( 'id' TEXT, 'mc_version' TEXT, 'modloader' TEXT, 'download_folder' TEXT );
519 INSERT INTO 'user_config' VALUES ( 'db_version', '0.4' ); 550 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' );
520 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )", 551 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )",
521 )?; 552 )?;
522 553
diff --git a/src/error.rs b/src/error.rs
index 612a2e2..794a919 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -39,7 +39,7 @@ impl fmt::Display for MLError {
39 ErrorType::DBError => write!(f, "Database: {}", self.message), 39 ErrorType::DBError => write!(f, "Database: {}", self.message),
40 ErrorType::ModError => write!(f, "Mod: {}", self.message), 40 ErrorType::ModError => write!(f, "Mod: {}", self.message),
41 ErrorType::LibToml => write!(f, "TOML"), 41 ErrorType::LibToml => write!(f, "TOML"),
42 ErrorType::LibSql => write!(f, "SQL"), 42 ErrorType::LibSql => write!(f, "SQL: {}", self.message),
43 ErrorType::LibReq => write!(f, "REQWEST"), 43 ErrorType::LibReq => write!(f, "REQWEST"),
44 ErrorType::LibChrono => write!(f, "Chrono error: {}", self.message), 44 ErrorType::LibChrono => write!(f, "Chrono error: {}", self.message),
45 ErrorType::IoError => write!(f, "IO"), 45 ErrorType::IoError => write!(f, "IO"),
diff --git a/src/files.rs b/src/files.rs
index 8c822b2..b9325ea 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -2,17 +2,17 @@ use std::{fs::{File, read_dir, remove_file, rename}, io::Write, collections::Has
2use futures_util::StreamExt; 2use futures_util::StreamExt;
3use reqwest::Client; 3use reqwest::Client;
4 4
5use crate::{List, modrinth::Version, db::{userlist_add_disabled_versions, mods_get_name}, config::Cfg, error::{MLE, MLError, ErrorType}}; 5use crate::{List, modrinth::Version, db::{userlist_add_disabled_versions, mods_get_title}, config::Cfg, error::{MLE, MLError, ErrorType}};
6 6
7pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { 7pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> {
8 8
9 let dl_path = String::from(&list.download_folder); 9 let dl_path = String::from(&list.download_folder);
10 10
11 println!("Download to directory from: {} ({})", list.id, dl_path); 11 println!(" â””Download mods to {}", dl_path);
12 12
13 for ver in versions { 13 for ver in versions {
14 let project_name = mods_get_name(config.clone(), &ver.project_id)?; 14 let project_name = mods_get_title(config.clone(), &ver.project_id)?;
15 print!("\t({})Download version {}", project_name, ver.id); 15 print!("\tâ””({})Download version {}", project_name, ver.id);
16 //Force flush of stdout, else print! doesn't print instantly 16 //Force flush of stdout, else print! doesn't print instantly
17 std::io::stdout().flush().unwrap(); 17 std::io::stdout().flush().unwrap();
18 let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap(); 18 let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap();
@@ -107,7 +107,7 @@ pub fn get_downloaded_versions(list: List) -> MLE<HashMap<String, String>> {
107 107
108pub fn clean_list_dir(list: &List) -> MLE<()> { 108pub fn clean_list_dir(list: &List) -> MLE<()> {
109 let dl_path = &list.download_folder; 109 let dl_path = &list.download_folder;
110 println!("Clean directory for: {}", list.id); 110 println!(" â””Clean directory for: {}", list.id);
111 for entry in std::fs::read_dir(dl_path)? { 111 for entry in std::fs::read_dir(dl_path)? {
112 let entry = entry?; 112 let entry = entry?;
113 std::fs::remove_file(entry.path())?; 113 std::fs::remove_file(entry.path())?;
diff --git a/src/input.rs b/src/input.rs
index 144f22a..17fc773 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -29,6 +29,7 @@ pub enum Cmd {
29 Download, 29 Download,
30 Io, 30 Io,
31 Version, 31 Version,
32 Setup,
32} 33}
33 34
34#[derive(Debug, Clone, PartialEq, Eq)] 35#[derive(Debug, Clone, PartialEq, Eq)]
@@ -58,7 +59,7 @@ impl Input {
58 args[0] = args[0].split_at(1).1; 59 args[0] = args[0].split_at(1).1;
59 60
60 let mut command: Option<Cmd> = None; 61 let mut command: Option<Cmd> = None;
61 62
62 let mut mod_options: Option<ModOptions> = None; 63 let mut mod_options: Option<ModOptions> = None;
63 let mut mod_id: Option<String> = None; 64 let mut mod_id: Option<String> = None;
64 let mut mod_version: Option<String> = None; 65 let mut mod_version: Option<String> = None;
@@ -77,7 +78,7 @@ impl Input {
77 let mut file: Option<String> = None; 78 let mut file: Option<String> = None;
78 79
79 for arg in args { 80 for arg in args {
80 let arg_split: Vec<&str> = arg.trim().split(" ").collect(); 81 let arg_split: Vec<&str> = arg.trim().split(' ').collect();
81 match arg_split[0] { 82 match arg_split[0] {
82 "v" | "version" => { 83 "v" | "version" => {
83 command = Some(Cmd::Version); 84 command = Some(Cmd::Version);
@@ -97,9 +98,8 @@ impl Input {
97 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list mod slug or id")); 98 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list mod slug or id"));
98 } 99 }
99 }, 100 },
101 //TODO impl this
100 "mv" => { 102 "mv" => {
101 command = Some(Cmd::Mod);
102 mod_options = Some(ModOptions::Add);
103 if arg_split.len() == 2 { 103 if arg_split.len() == 2 {
104 mod_version = Some(String::from(arg_split[1])); 104 mod_version = Some(String::from(arg_split[1]));
105 } else { 105 } else {
@@ -195,6 +195,9 @@ impl Input {
195 "f" => { 195 "f" => {
196 file = Some(String::from(arg_split[1])); 196 file = Some(String::from(arg_split[1]));
197 }, 197 },
198 "setup" => {
199 command = Some(Cmd::Setup);
200 }
198 _ => return Err(MLError::new(ErrorType::ArgumentError, format!("Unknown Argument ({})", arg_split[0]).as_str())), 201 _ => return Err(MLError::new(ErrorType::ArgumentError, format!("Unknown Argument ({})", arg_split[0]).as_str())),
199 } 202 }
200 } 203 }
@@ -240,11 +243,12 @@ fn check_mod(mut input: Input, config: Cfg) -> MLE<Input> {
240 match input.clone().mod_options.unwrap() { 243 match input.clone().mod_options.unwrap() {
241 ModOptions::Add => { 244 ModOptions::Add => {
242 if input.mod_id.is_none() && input.mod_version.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "No mod id/slug or version id")); }; 245 if input.mod_id.is_none() && input.mod_version.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "No mod id/slug or version id")); };
243 if input.list_id.is_none() { input.list = Some(get_current_list(config.clone())?); }; 246 if input.list_id.is_none() { input.list = Some(get_current_list(config)?); };
244 Ok(input) 247 Ok(input)
245 }, 248 },
246 ModOptions::Remove => { 249 ModOptions::Remove => {
247 if input.mod_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_MODID")); }; 250 if input.mod_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_MODID")); };
251 if input.list_id.is_none() { input.list = Some(get_current_list(config)?); };
248 Ok(input) 252 Ok(input)
249 }, 253 },
250 } 254 }
@@ -274,7 +278,6 @@ async fn check_list(mut input: Input, config: Cfg) -> MLE<Input> {
274 Ok(input) 278 Ok(input)
275 }, 279 },
276 ListOptions::Change => { 280 ListOptions::Change => {
277 //TODO check if no change
278 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); }; 281 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
279 Ok(input) 282 Ok(input)
280 }, 283 },
@@ -292,7 +295,7 @@ async fn check_list(mut input: Input, config: Cfg) -> MLE<Input> {
292fn input_from() { 295fn input_from() {
293 let config = Cfg::init("modlist.toml").unwrap(); 296 let config = Cfg::init("modlist.toml").unwrap();
294 assert_eq!( 297 assert_eq!(
295 Input::from(config.clone(), vec![String::from("-la test -lv 1.19.3")]).unwrap(), 298 Input::from(config, vec![String::from("-la test -lv 1.19.3")]).unwrap(),
296 Input { 299 Input {
297 command: Some(Cmd::List), 300 command: Some(Cmd::List),
298 mod_options: None, 301 mod_options: None,
@@ -310,7 +313,7 @@ fn input_from() {
310 modloader: None, 313 modloader: None,
311 directory: None, 314 directory: None,
312 io_options: None, 315 io_options: None,
313 file: None 316 file: None,
314 } 317 }
315 ); 318 );
316 319
diff --git a/src/main.rs b/src/main.rs
index d177c3e..32727c7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,15 +1,20 @@
1use std::{env, process}; 1use std::{env, process};
2 2
3use modlist::{config::Cfg, input::{get_input, Cmd}, update, download, list, io, modification}; 3use modlist::{config::Cfg, input::{get_input, Cmd}, update, download, list, io, modification, setup};
4 4
5#[tokio::main] 5#[tokio::main]
6async fn main() { 6async fn main() {
7 let config = Cfg::init("modlist.toml").unwrap(); 7 let config = Cfg::init("modlist.toml").unwrap();
8 8
9 let mut args: Vec<String> = env::args().collect(); 9 let mut args: Vec<String> = env::args().collect();
10 args.reverse(); 10 args.reverse();
11 args.pop(); 11 args.pop();
12 args.reverse(); 12 args.reverse();
13
14 if args.is_empty() {
15 println!("Please enter an argument");
16 process::exit(1);
17 };
13 18
14 let input = match get_input(config.clone(), args).await { 19 let input = match get_input(config.clone(), args).await {
15 Ok(i) => i, 20 Ok(i) => i,
@@ -19,8 +24,6 @@ async fn main() {
19 } 24 }
20 }; 25 };
21 26
22 //dbg!(&input);
23
24 match input.clone().command.unwrap() { 27 match input.clone().command.unwrap() {
25 Cmd::Mod => { 28 Cmd::Mod => {
26 modification(config, input).await 29 modification(config, input).await
@@ -41,6 +44,9 @@ async fn main() {
41 show_version(); 44 show_version();
42 Ok(()) 45 Ok(())
43 }, 46 },
47 Cmd::Setup => {
48 setup(config).await
49 },
44 }.unwrap() 50 }.unwrap()
45} 51}
46 52
diff --git a/tests/db.rs b/tests/db.rs
deleted file mode 100644
index 992899f..0000000
--- a/tests/db.rs
+++ /dev/null
@@ -1,249 +0,0 @@
1use std::{fs::{File, create_dir_all, remove_dir_all}, path::Path, sync::Once};
2
3use modlist::{config::{Cfg, Apis}, db::{mods_insert, db_setup, mods_get_all_ids, mods_get_id, mods_remove, userlist_insert, lists_insert, userlist_get_all_ids, userlist_remove, mods_get_versions, userlist_get_applicable_versions, lists_remove, lists_get, lists_get_all_ids, userlist_get_all_current_version_ids, userlist_change_versions, s_userlist_update_download, userlist_get_all_downloads, config_change_current_list, config_get_current_list, s_config_update_version, s_config_create_version, s_config_get_version}, Modloader, List};
4
5static INIT: Once = Once::new();
6
7fn setup() -> Cfg {
8 let db_pathstr = "./test_tmp/db";
9
10 let config = Cfg { data: String::from(db_pathstr), apis: Apis { modrinth: String::from("-") } };
11
12 INIT.call_once(|| {
13 let db_path = Path::new(db_pathstr);
14 if db_path.exists() { remove_dir_all(db_pathstr).unwrap(); };
15 create_dir_all(db_path).unwrap();
16 let db_filestr = format!("{}/data.db", db_pathstr);
17 File::create(db_filestr).unwrap();
18 println!("INIT TEST DB");
19 db_setup(config.clone()).unwrap();
20 });
21 config
22}
23
24#[test]
25fn test_mods_insert() {
26 let config = setup();
27
28 mods_insert(config.clone(), String::from("I"), String::from("INSERT_TEST"), vec![String::from("INSERT_VER1"), String::from("INSERT_VER2")]).unwrap();
29 let ids = mods_get_all_ids(config).unwrap();
30
31 assert!(ids.contains(&String::from("I")));
32}
33
34#[test]
35fn test_mods_get_all_ids() {
36 let config = setup();
37
38 mods_insert(config.clone(), String::from("GAI1"), String::from("GETALLIDS_TEST1"), vec![String::from("GAI1_VER1"), String::from("GAI1_VER2")]).unwrap();
39 mods_insert(config.clone(), String::from("GAI2"), String::from("GETALLIDS_TEST2"), vec![String::from("GAI2_VER1"), String::from("GAI2_VER2")]).unwrap();
40 let ids = mods_get_all_ids(config).unwrap();
41
42 assert!(ids.contains(&String::from("GAI1")));
43 assert!(ids.contains(&String::from("GAI2")));
44}
45
46#[test]
47fn test_mods_get_id() {
48 let config = setup();
49
50 mods_insert(config, String::from("GI"), String::from("GETID_TEST"), vec![String::from("GI_VER1"), String::from("GI_VER2")]).unwrap();
51}
52
53#[test]
54fn test_mods_remove() {
55 let config = setup();
56
57 mods_insert(config.clone(), String::from("R"), String::from("REMOVE_TEST"), vec![String::from("R_VER1"), String::from("R_VER2")]).unwrap();
58 let ids = mods_get_all_ids(config.clone()).unwrap();
59 assert!(ids.contains(&String::from("R")));
60 mods_remove(config.clone(), String::from("R")).unwrap();
61 assert!(mods_get_id(config, String::from("REMOVE_TEST")).is_err());
62}
63
64#[test]
65fn test_mods_get_versions() {
66 let config = setup();
67
68 mods_insert(config.clone(), String::from("M_GVs1"), String::from("M_GVs_TEST1"), vec![String::from("M_GVs1_VER1"), String::from("M_GVs1_VER2")]).unwrap();
69 mods_insert(config.clone(), String::from("M_GVs2"), String::from("M_GVs_TEST2"), vec![String::from("M_GVs2_VER1"), String::from("M_GVs2_VER2")]).unwrap();
70 let versions = mods_get_versions(config, vec![String::from("M_GVs1"), String::from("M_GVs2")]).unwrap();
71
72 assert!(versions.contains(&modlist::db::DBModlistVersions { mod_id: String::from("M_GVs1"), versions: String::from("M_GVs1_VER1|M_GVs1_VER2") }));
73 assert!(versions.contains(&modlist::db::DBModlistVersions { mod_id: String::from("M_GVs2"), versions: String::from("M_GVs2_VER1|M_GVs2_VER2") }));
74}
75
76//user_list
77#[test]
78fn test_userlist_insert() {
79 let config = setup();
80
81 lists_insert(config.clone(), String::from("UL_I_LIST"), String::from("UL_I_MC"), Modloader::Fabric, String::from("UL_I_FOLDER")).unwrap();
82 userlist_insert(config, String::from("UL_I_LIST"), String::from("UL_I"), String::from("UL_I_VER1"), vec![String::from("UL_I_VER1"), String::from("UL_I_VER2")], String::from("localhost:8080/dl/UL_I_VER1.test")).unwrap();
83}
84
85#[test]
86fn test_userlist_get_all_ids() {
87 let config = setup();
88
89 lists_insert(config.clone(), String::from("UL_GAI_LIST"), String::from("UL_GAI_MC"), Modloader::Fabric, String::from("UL_GAI_FOLDER")).unwrap();
90 userlist_insert(config.clone(), String::from("UL_GAI_LIST"), String::from("UL_GAI1"), String::from("UL_GAI1_VER1"), vec![String::from("UL_GAI2_VER1"), String::from("UL_GAI1_VER2")], String::from("localhost:8080/dl/UL_GAI1_VER1.test")).unwrap();
91 userlist_insert(config.clone(), String::from("UL_GAI_LIST"), String::from("UL_GAI2"), String::from("UL_GAI2_VER1"), vec![String::from("UL_GAI2_VER1"), String::from("UL_GAI2_VER2")], String::from("localhost:8080/dl/UL_GAI2_VER1.test")).unwrap();
92 let ids = userlist_get_all_ids(config, String::from("UL_GAI_LIST")).unwrap();
93
94 assert!(ids.contains(&String::from("UL_GAI1")));
95 assert!(ids.contains(&String::from("UL_GAI2")));
96}
97
98#[test]
99fn test_userlist_remove() {
100 let config = setup();
101
102 lists_insert(config.clone(), String::from("UL_R_LIST"), String::from("UL_R_MC"), Modloader::Fabric, String::from("UL_R_FOLDER")).unwrap();
103 userlist_insert(config.clone(), String::from("UL_R_LIST"), String::from("UL_R"), String::from("UL_R_VER1"), vec![String::from("UL_R_VER1"), String::from("UL_R_VER2")], String::from("localhost:8080/dl/UL_R_VER1.test")).unwrap();
104 let ids = userlist_get_all_ids(config.clone(), String::from("UL_R_LIST")).unwrap();
105 assert!(ids.contains(&String::from("UL_R")));
106 userlist_remove(config.clone(), String::from("UL_R_LIST"), String::from("UL_R")).unwrap();
107 assert!(userlist_get_all_ids(config, String::from("UL_R_LIST")).is_err())
108}
109
110#[test]
111fn test_userlist_get_applicable_versions() {
112 let config = setup();
113
114 lists_insert(config.clone(), String::from("UL_GAV_LIST"), String::from("UL_GAV_MC"), Modloader::Fabric, String::from("UL_GAV_FOLDER")).unwrap();
115 userlist_insert(config.clone(), String::from("UL_GAV_LIST"), String::from("UL_GAV"), String::from("UL_GAV_VER1"), vec![String::from("UL_GAV_VER1"), String::from("UL_GAV_VER2")], String::from("localhost:8080/dl/UL_GAV_VER1.test")).unwrap();
116 assert_eq!(userlist_get_applicable_versions(config, String::from("UL_GAV_LIST"), String::from("UL_GAV")).unwrap(), String::from("UL_GAV_VER1|UL_GAV_VER2"));
117}
118
119#[test]
120fn test_userlist_get_all_current_version_ids() {
121 let config = setup();
122
123 lists_insert(config.clone(), String::from("UL_GACVI_LIST"), String::from("UL_GACVI_MC"), Modloader::Fabric, String::from("UL_GACVI_FOLDER")).unwrap();
124 userlist_insert(config.clone(), String::from("UL_GACVI_LIST"), String::from("UL_GACVI1"), String::from("UL_GACVI1_VER1"), vec![String::from("UL_GACVI2_VER1"), String::from("UL_GACVI1_VER2")], String::from("localhost:8080/dl/UL_GACVI1_VER1.test")).unwrap();
125 userlist_insert(config.clone(), String::from("UL_GACVI_LIST"), String::from("UL_GACVI2"), String::from("UL_GACVI2_VER1"), vec![String::from("UL_GACVI2_VER1"), String::from("UL_GACVI2_VER2")], String::from("localhost:8080/dl/UL_GACVI2_VER1.test")).unwrap();
126
127 let ids = userlist_get_all_current_version_ids(config, String::from("UL_GACVI_LIST")).unwrap();
128
129 assert!(ids.contains(&String::from("UL_GACVI1_VER1")));
130 assert!(ids.contains(&String::from("UL_GACVI2_VER1")));
131}
132
133#[test]
134fn test_userlist_change_versions() {
135 let config = setup();
136
137 lists_insert(config.clone(), String::from("UL_CV_LIST"), String::from("UL_CV_MC"), Modloader::Fabric, String::from("UL_CV_FOLDER")).unwrap();
138 userlist_insert(config.clone(), String::from("UL_CV_LIST"), String::from("UL_CV"), String::from("UL_CV_VER1"), vec![String::from("UL_CV_VER1"), String::from("UL_CV_VER2")], String::from("localhost:8080/dl/UL_CV_VER1.test")).unwrap();
139 let versions = userlist_get_all_current_version_ids(config.clone(), String::from("UL_CV_LIST")).unwrap();
140 assert!(versions.contains(&String::from("UL_CV_VER1")));
141
142 userlist_change_versions(config.clone(), String::from("UL_CV_LIST"), String::from("UL_CV_VER3"), String::from("UL_CV_VER1|UL_CV_VER2|UL_CV_VER3"), String::from("localhost:8080/dl/UL_CV_VER3.test"), String::from("UL_CV")).unwrap();
143 let versions = userlist_get_all_current_version_ids(config, String::from("UL_CV_LIST")).unwrap();
144 assert!(!versions.contains(&String::from("UL_CV_VER1")));
145 assert!(versions.contains(&String::from("UL_CV_VER3")));
146}
147
148#[test]
149fn test_userlist_get_all_downloads() {
150 let config = setup();
151
152 lists_insert(config.clone(), String::from("UL_GAD_LIST"), String::from("UL_GAD_MC"), Modloader::Fabric, String::from("UL_GAD_FOLDER")).unwrap();
153 userlist_insert(config.clone(), String::from("UL_GAD_LIST"), String::from("UL_GAD1"), String::from("UL_GAD1_VER1"), vec![String::from("UL_GAD1_VER1"), String::from("UL_GAD1_VER1")], String::from("localhost:8080/dl/UL_GAD1_VER1.test")).unwrap();
154 userlist_insert(config.clone(), String::from("UL_GAD_LIST"), String::from("UL_GAD2"), String::from("UL_GAD2_VER1"), vec![String::from("UL_GAD2_VER1"), String::from("UL_GAD2_VER1")], String::from("localhost:8080/dl/UL_GAD2_VER1.test")).unwrap();
155 let links = userlist_get_all_downloads(config, String::from("UL_GAD_LIST")).unwrap();
156
157 assert!(links.contains(&String::from("localhost:8080/dl/UL_GAD1_VER1.test")));
158 assert!(links.contains(&String::from("localhost:8080/dl/UL_GAD2_VER1.test")));
159}
160
161
162//lists
163#[test]
164fn test_lists_insert() {
165 let config = setup();
166
167 lists_insert(config, String::from("L_I_LIST"), String::from("L_I_MC"), Modloader::Fabric, String::from("L_I_FOLDER")).unwrap();
168}
169
170#[test]
171fn test_lists_remove() {
172 let config = setup();
173
174 lists_insert(config.clone(), String::from("L_R_LIST"), String::from("L_R_MC"), Modloader::Fabric, String::from("L_R_FOLDER")).unwrap();
175 lists_remove(config, String::from("L_R_LIST")).unwrap();
176}
177
178#[test]
179fn test_lists_get() {
180 let config = setup();
181
182 lists_insert(config.clone(), String::from("L_G_LIST"), String::from("L_G_MC"), Modloader::Fabric, String::from("L_G_FOLDER")).unwrap();
183
184 assert_eq!(lists_get(config, String::from("L_G_LIST")).unwrap(), List { id: String::from("L_G_LIST"), mc_version: String::from("L_G_MC"), modloader: Modloader::Fabric, download_folder: String::from("L_G_FOLDER") });
185}
186
187#[test]
188fn test_lists_get_all_ids() {
189 let config = setup();
190
191 lists_insert(config.clone(), String::from("L_GAI1_LIST"), String::from("L_GAI1_MC"), Modloader::Fabric, String::from("L_GAI1_FOLDER")).unwrap();
192 lists_insert(config.clone(), String::from("L_GAI2_LIST"), String::from("L_GAI2_MC"), Modloader::Fabric, String::from("L_GAI2_FOLDER")).unwrap();
193 let ids = lists_get_all_ids(config).unwrap();
194
195 assert!(ids.contains(&String::from("L_GAI1_LIST")));
196 assert!(ids.contains(&String::from("L_GAI2_LIST")));
197}
198
199//config
200#[test]
201fn test_config_change_current_list() {
202 let config = setup();
203
204 config_change_current_list(config, String::from("C_CCL_LIST")).unwrap();
205}
206
207#[test]
208fn test_config_get_current_list() {
209 let config = setup();
210
211 config_change_current_list(config.clone(), String::from("C_GCL_LIST")).unwrap();
212 assert_eq!(config_get_current_list(config).unwrap(), String::from("C_GCL_LIST"));
213}
214
215//setup
216#[test]
217fn test_s_userlist_update_download() {
218 let config = setup();
219
220 lists_insert(config.clone(), String::from("UL_UD_LIST"), String::from("UL_UD_MC"), Modloader::Fabric, String::from("UL_UD_FOLDER")).unwrap();
221 userlist_insert(config.clone(), String::from("UL_UD_LIST"), String::from("UL_UD"), String::from("UL_UD_VER1"), vec![String::from("UL_UD_VER1"), String::from("UL_UD_VER1")], String::from("localhost:8080/dl/UL_UD_VER1.test")).unwrap();
222 s_userlist_update_download(config.clone(), String::from("UL_UD_LIST"), String::from("UL_UD"), String::from("localhost:8080/dl/UL_UD_VER1X.test")).unwrap();
223 let links = userlist_get_all_downloads(config, String::from("UL_UD_LIST")).unwrap();
224
225 assert!(links.contains(&String::from("localhost:8080/dl/UL_UD_VER1X.test")));
226 assert!(!links.contains(&String::from("localhost:8080/dl/UL_UD_VER1.test")));
227}
228
229#[test]
230fn test_s_config_create_version() {
231 let config = setup();
232
233 s_config_create_version(config).unwrap();
234}
235
236#[test]
237fn test_s_config_update_version() {
238 let config = setup();
239
240 s_config_update_version(config, String::from("S_C_UV")).unwrap();
241}
242
243#[test]
244fn test_s_config_get_version() {
245 let config = setup();
246
247 s_config_update_version(config.clone(), String::from("S_C_GV")).unwrap();
248 assert_eq!(s_config_get_version(config).unwrap(), String::from("S_C_GV"));
249}