diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Cargo.lock | 201 | ||||
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | config.ini | 1 | ||||
-rw-r--r-- | data.db | bin | 36864 -> 36864 bytes | |||
-rw-r--r-- | src/apis/modrinth.rs | 23 | ||||
-rw-r--r-- | src/commands/modification.rs | 11 | ||||
-rw-r--r-- | src/commands/update.rs | 140 | ||||
-rw-r--r-- | src/config.rs | 1 | ||||
-rw-r--r-- | src/db.rs | 32 | ||||
-rw-r--r-- | src/input.rs | 10 | ||||
-rw-r--r-- | tests/db/mod.rs | 4 | ||||
-rw-r--r-- | tests/db_integration.rs | 1 |
13 files changed, 387 insertions, 45 deletions
@@ -1,4 +1,4 @@ | |||
1 | /target | 1 | /target |
2 | /api-tests | 2 | /api-tests |
3 | /data | 3 | /dl |
4 | .planmodlist.autosave.xopp | 4 | .planmodlist.autosave.xopp |
@@ -14,6 +14,15 @@ dependencies = [ | |||
14 | ] | 14 | ] |
15 | 15 | ||
16 | [[package]] | 16 | [[package]] |
17 | name = "android_system_properties" | ||
18 | version = "0.1.5" | ||
19 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
20 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" | ||
21 | dependencies = [ | ||
22 | "libc", | ||
23 | ] | ||
24 | |||
25 | [[package]] | ||
17 | name = "async-trait" | 26 | name = "async-trait" |
18 | version = "0.1.58" | 27 | version = "0.1.58" |
19 | source = "registry+https://github.com/rust-lang/crates.io-index" | 28 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -76,6 +85,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
76 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | 85 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
77 | 86 | ||
78 | [[package]] | 87 | [[package]] |
88 | name = "chrono" | ||
89 | version = "0.4.22" | ||
90 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
91 | checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" | ||
92 | dependencies = [ | ||
93 | "iana-time-zone", | ||
94 | "js-sys", | ||
95 | "num-integer", | ||
96 | "num-traits", | ||
97 | "time", | ||
98 | "wasm-bindgen", | ||
99 | "winapi", | ||
100 | ] | ||
101 | |||
102 | [[package]] | ||
103 | name = "codespan-reporting" | ||
104 | version = "0.11.1" | ||
105 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
106 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" | ||
107 | dependencies = [ | ||
108 | "termcolor", | ||
109 | "unicode-width", | ||
110 | ] | ||
111 | |||
112 | [[package]] | ||
79 | name = "config" | 113 | name = "config" |
80 | version = "0.13.2" | 114 | version = "0.13.2" |
81 | source = "registry+https://github.com/rust-lang/crates.io-index" | 115 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -130,6 +164,50 @@ dependencies = [ | |||
130 | ] | 164 | ] |
131 | 165 | ||
132 | [[package]] | 166 | [[package]] |
167 | name = "cxx" | ||
168 | version = "1.0.80" | ||
169 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
170 | checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" | ||
171 | dependencies = [ | ||
172 | "cc", | ||
173 | "cxxbridge-flags", | ||
174 | "cxxbridge-macro", | ||
175 | "link-cplusplus", | ||
176 | ] | ||
177 | |||
178 | [[package]] | ||
179 | name = "cxx-build" | ||
180 | version = "1.0.80" | ||
181 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
182 | checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" | ||
183 | dependencies = [ | ||
184 | "cc", | ||
185 | "codespan-reporting", | ||
186 | "once_cell", | ||
187 | "proc-macro2", | ||
188 | "quote", | ||
189 | "scratch", | ||
190 | "syn", | ||
191 | ] | ||
192 | |||
193 | [[package]] | ||
194 | name = "cxxbridge-flags" | ||
195 | version = "1.0.80" | ||
196 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
197 | checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" | ||
198 | |||
199 | [[package]] | ||
200 | name = "cxxbridge-macro" | ||
201 | version = "1.0.80" | ||
202 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
203 | checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" | ||
204 | dependencies = [ | ||
205 | "proc-macro2", | ||
206 | "quote", | ||
207 | "syn", | ||
208 | ] | ||
209 | |||
210 | [[package]] | ||
133 | name = "digest" | 211 | name = "digest" |
134 | version = "0.10.5" | 212 | version = "0.10.5" |
135 | source = "registry+https://github.com/rust-lang/crates.io-index" | 213 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -209,6 +287,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
209 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" | 287 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" |
210 | 288 | ||
211 | [[package]] | 289 | [[package]] |
290 | name = "futures-macro" | ||
291 | version = "0.3.25" | ||
292 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
293 | checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" | ||
294 | dependencies = [ | ||
295 | "proc-macro2", | ||
296 | "quote", | ||
297 | "syn", | ||
298 | ] | ||
299 | |||
300 | [[package]] | ||
212 | name = "futures-sink" | 301 | name = "futures-sink" |
213 | version = "0.3.25" | 302 | version = "0.3.25" |
214 | source = "registry+https://github.com/rust-lang/crates.io-index" | 303 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -227,9 +316,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
227 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" | 316 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" |
228 | dependencies = [ | 317 | dependencies = [ |
229 | "futures-core", | 318 | "futures-core", |
319 | "futures-macro", | ||
230 | "futures-task", | 320 | "futures-task", |
231 | "pin-project-lite", | 321 | "pin-project-lite", |
232 | "pin-utils", | 322 | "pin-utils", |
323 | "slab", | ||
233 | ] | 324 | ] |
234 | 325 | ||
235 | [[package]] | 326 | [[package]] |
@@ -250,7 +341,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" | |||
250 | dependencies = [ | 341 | dependencies = [ |
251 | "cfg-if", | 342 | "cfg-if", |
252 | "libc", | 343 | "libc", |
253 | "wasi", | 344 | "wasi 0.11.0+wasi-snapshot-preview1", |
254 | ] | 345 | ] |
255 | 346 | ||
256 | [[package]] | 347 | [[package]] |
@@ -362,6 +453,30 @@ dependencies = [ | |||
362 | ] | 453 | ] |
363 | 454 | ||
364 | [[package]] | 455 | [[package]] |
456 | name = "iana-time-zone" | ||
457 | version = "0.1.53" | ||
458 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
459 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" | ||
460 | dependencies = [ | ||
461 | "android_system_properties", | ||
462 | "core-foundation-sys", | ||
463 | "iana-time-zone-haiku", | ||
464 | "js-sys", | ||
465 | "wasm-bindgen", | ||
466 | "winapi", | ||
467 | ] | ||
468 | |||
469 | [[package]] | ||
470 | name = "iana-time-zone-haiku" | ||
471 | version = "0.1.1" | ||
472 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
473 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" | ||
474 | dependencies = [ | ||
475 | "cxx", | ||
476 | "cxx-build", | ||
477 | ] | ||
478 | |||
479 | [[package]] | ||
365 | name = "idna" | 480 | name = "idna" |
366 | version = "0.3.0" | 481 | version = "0.3.0" |
367 | source = "registry+https://github.com/rust-lang/crates.io-index" | 482 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -435,6 +550,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
435 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" | 550 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" |
436 | 551 | ||
437 | [[package]] | 552 | [[package]] |
553 | name = "link-cplusplus" | ||
554 | version = "1.0.7" | ||
555 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
556 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" | ||
557 | dependencies = [ | ||
558 | "cc", | ||
559 | ] | ||
560 | |||
561 | [[package]] | ||
438 | name = "linked-hash-map" | 562 | name = "linked-hash-map" |
439 | version = "0.5.6" | 563 | version = "0.5.6" |
440 | source = "registry+https://github.com/rust-lang/crates.io-index" | 564 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -485,7 +609,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" | |||
485 | dependencies = [ | 609 | dependencies = [ |
486 | "libc", | 610 | "libc", |
487 | "log", | 611 | "log", |
488 | "wasi", | 612 | "wasi 0.11.0+wasi-snapshot-preview1", |
489 | "windows-sys 0.42.0", | 613 | "windows-sys 0.42.0", |
490 | ] | 614 | ] |
491 | 615 | ||
@@ -493,7 +617,9 @@ dependencies = [ | |||
493 | name = "modlist" | 617 | name = "modlist" |
494 | version = "0.1.0" | 618 | version = "0.1.0" |
495 | dependencies = [ | 619 | dependencies = [ |
620 | "chrono", | ||
496 | "config", | 621 | "config", |
622 | "futures-util", | ||
497 | "reqwest", | 623 | "reqwest", |
498 | "serde", | 624 | "serde", |
499 | "serde_json", | 625 | "serde_json", |
@@ -530,6 +656,25 @@ dependencies = [ | |||
530 | ] | 656 | ] |
531 | 657 | ||
532 | [[package]] | 658 | [[package]] |
659 | name = "num-integer" | ||
660 | version = "0.1.45" | ||
661 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
662 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" | ||
663 | dependencies = [ | ||
664 | "autocfg", | ||
665 | "num-traits", | ||
666 | ] | ||
667 | |||
668 | [[package]] | ||
669 | name = "num-traits" | ||
670 | version = "0.2.15" | ||
671 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
672 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" | ||
673 | dependencies = [ | ||
674 | "autocfg", | ||
675 | ] | ||
676 | |||
677 | [[package]] | ||
533 | name = "num_cpus" | 678 | name = "num_cpus" |
534 | version = "1.13.1" | 679 | version = "1.13.1" |
535 | source = "registry+https://github.com/rust-lang/crates.io-index" | 680 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -762,6 +907,7 @@ dependencies = [ | |||
762 | "serde_urlencoded", | 907 | "serde_urlencoded", |
763 | "tokio", | 908 | "tokio", |
764 | "tokio-native-tls", | 909 | "tokio-native-tls", |
910 | "tokio-util", | ||
765 | "tower-service", | 911 | "tower-service", |
766 | "url", | 912 | "url", |
767 | "wasm-bindgen", | 913 | "wasm-bindgen", |
@@ -814,6 +960,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
814 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" | 960 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" |
815 | 961 | ||
816 | [[package]] | 962 | [[package]] |
963 | name = "scratch" | ||
964 | version = "1.0.2" | ||
965 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
966 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" | ||
967 | |||
968 | [[package]] | ||
817 | name = "security-framework" | 969 | name = "security-framework" |
818 | version = "2.7.0" | 970 | version = "2.7.0" |
819 | source = "registry+https://github.com/rust-lang/crates.io-index" | 971 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -926,9 +1078,9 @@ dependencies = [ | |||
926 | 1078 | ||
927 | [[package]] | 1079 | [[package]] |
928 | name = "sqlite" | 1080 | name = "sqlite" |
929 | version = "0.27.3" | 1081 | version = "0.28.0" |
930 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" |
931 | checksum = "e66cb949f931ece6201d72bffad3f3601b94998a345793713dd13af70a77c185" | 1083 | checksum = "125b4c6f5428ee2fc895b6c3633f7761084028481bca9a02dce6477d80eff083" |
932 | dependencies = [ | 1084 | dependencies = [ |
933 | "libc", | 1085 | "libc", |
934 | "sqlite3-sys", | 1086 | "sqlite3-sys", |
@@ -980,6 +1132,15 @@ dependencies = [ | |||
980 | ] | 1132 | ] |
981 | 1133 | ||
982 | [[package]] | 1134 | [[package]] |
1135 | name = "termcolor" | ||
1136 | version = "1.1.3" | ||
1137 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1138 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" | ||
1139 | dependencies = [ | ||
1140 | "winapi-util", | ||
1141 | ] | ||
1142 | |||
1143 | [[package]] | ||
983 | name = "thiserror" | 1144 | name = "thiserror" |
984 | version = "1.0.37" | 1145 | version = "1.0.37" |
985 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1000,6 +1161,17 @@ dependencies = [ | |||
1000 | ] | 1161 | ] |
1001 | 1162 | ||
1002 | [[package]] | 1163 | [[package]] |
1164 | name = "time" | ||
1165 | version = "0.1.44" | ||
1166 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1167 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" | ||
1168 | dependencies = [ | ||
1169 | "libc", | ||
1170 | "wasi 0.10.0+wasi-snapshot-preview1", | ||
1171 | "winapi", | ||
1172 | ] | ||
1173 | |||
1174 | [[package]] | ||
1003 | name = "tinyvec" | 1175 | name = "tinyvec" |
1004 | version = "1.6.0" | 1176 | version = "1.6.0" |
1005 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1144,6 +1316,12 @@ dependencies = [ | |||
1144 | ] | 1316 | ] |
1145 | 1317 | ||
1146 | [[package]] | 1318 | [[package]] |
1319 | name = "unicode-width" | ||
1320 | version = "0.1.10" | ||
1321 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1322 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" | ||
1323 | |||
1324 | [[package]] | ||
1147 | name = "url" | 1325 | name = "url" |
1148 | version = "2.3.1" | 1326 | version = "2.3.1" |
1149 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1178,6 +1356,12 @@ dependencies = [ | |||
1178 | 1356 | ||
1179 | [[package]] | 1357 | [[package]] |
1180 | name = "wasi" | 1358 | name = "wasi" |
1359 | version = "0.10.0+wasi-snapshot-preview1" | ||
1360 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1361 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" | ||
1362 | |||
1363 | [[package]] | ||
1364 | name = "wasi" | ||
1181 | version = "0.11.0+wasi-snapshot-preview1" | 1365 | version = "0.11.0+wasi-snapshot-preview1" |
1182 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1183 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | 1367 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" |
@@ -1275,6 +1459,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1275 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | 1459 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" |
1276 | 1460 | ||
1277 | [[package]] | 1461 | [[package]] |
1462 | name = "winapi-util" | ||
1463 | version = "0.1.5" | ||
1464 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1465 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" | ||
1466 | dependencies = [ | ||
1467 | "winapi", | ||
1468 | ] | ||
1469 | |||
1470 | [[package]] | ||
1278 | name = "winapi-x86_64-pc-windows-gnu" | 1471 | name = "winapi-x86_64-pc-windows-gnu" |
1279 | version = "0.4.0" | 1472 | version = "0.4.0" |
1280 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -6,9 +6,11 @@ edition = "2021" | |||
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 |
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | reqwest = { version = "0.11", features = ["json"] } | 9 | reqwest = { version = "0.11", features = ["json", "stream"] } |
10 | tokio = { version = "1", features = ["full"] } | 10 | tokio = { version = "1", features = ["full"] } |
11 | serde = { version = "1.0", features = ["derive"] } | 11 | serde = { version = "1.0", features = ["derive"] } |
12 | serde_json = "1.0.87" | 12 | serde_json = "1.0.87" |
13 | config = "0.13.2" | 13 | config = "0.13.2" |
14 | sqlite = "0.27.3" | 14 | sqlite = "0.28.0" |
15 | futures-util = "0.3.14" | ||
16 | chrono = "0.4.22" | ||
@@ -1,5 +1,6 @@ | |||
1 | data = "./" | 1 | data = "./" |
2 | clean_remove = false | 2 | clean_remove = false |
3 | downloads = "./dl" | ||
3 | 4 | ||
4 | [apis] | 5 | [apis] |
5 | modrinth = "http://localhost:8080/" | 6 | modrinth = "http://localhost:8080/" |
Binary files differ | |||
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs index 0c3eca5..c71b47f 100644 --- a/src/apis/modrinth.rs +++ b/src/apis/modrinth.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::io::{Error, ErrorKind}; | ||
2 | use chrono::{DateTime, FixedOffset}; | ||
1 | use serde::Deserialize; | 3 | use serde::Deserialize; |
2 | 4 | ||
3 | use crate::{Modloader, List}; | 5 | use crate::{Modloader, List}; |
@@ -153,3 +155,24 @@ pub async fn versions(api: String, id: String, list: List) -> Vec<Version> { | |||
153 | 155 | ||
154 | serde_json::from_slice(&data.await.unwrap()).unwrap() | 156 | serde_json::from_slice(&data.await.unwrap()).unwrap() |
155 | } | 157 | } |
158 | |||
159 | pub fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { | ||
160 | match versions.len() { | ||
161 | 0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))), | ||
162 | //TODO compare publish dates | ||
163 | 1.. => { | ||
164 | let mut times: Vec<(String, DateTime<FixedOffset>)> = vec![]; | ||
165 | for ver in versions { | ||
166 | let stamp = DateTime::parse_from_rfc3339(&ver.date_published)?; | ||
167 | times.push((ver.id, stamp)) | ||
168 | } | ||
169 | dbg!(×); | ||
170 | times.sort_by_key(|t| t.1); | ||
171 | times.reverse(); | ||
172 | dbg!(×); | ||
173 | println!("CW: {}", times[0].0); | ||
174 | Ok(times[0].0.to_string()) | ||
175 | }, | ||
176 | _ => panic!("available_versions should never be negative"), | ||
177 | } | ||
178 | } | ||
diff --git a/src/commands/modification.rs b/src/commands/modification.rs index 43e2180..b90c82c 100644 --- a/src/commands/modification.rs +++ b/src/commands/modification.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::io::{Error, ErrorKind}; | 1 | use std::io::{Error, ErrorKind}; |
2 | 2 | ||
3 | use crate::{modrinth::{project, versions, Version}, config::Cfg, db::{insert_mod, remove_mod_from_list, get_mod_id, insert_mod_in_list, get_mods, get_mods_from_list}, input::Input, get_current_list}; | 3 | use crate::{modrinth::{project, versions, extract_current_version}, config::Cfg, db::{insert_mod, remove_mod_from_list, get_mod_id, insert_mod_in_list, get_mods, get_mods_from_list}, input::Input, get_current_list}; |
4 | 4 | ||
5 | pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { | 5 | pub async fn modification(config: Cfg, args: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { |
6 | 6 | ||
@@ -77,12 +77,3 @@ fn remove(config: Cfg, args: Vec<String>) -> Result<(), Box<dyn std::error::Erro | |||
77 | Ok(()) => Ok(()), | 77 | Ok(()) => Ok(()), |
78 | } | 78 | } |
79 | } | 79 | } |
80 | |||
81 | fn extract_current_version(versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { | ||
82 | match versions.len() { | ||
83 | 0 => Err(Box::new(Error::new(ErrorKind::NotFound, "NO_VERSIONS_AVAILABLE"))), | ||
84 | //TODO compare publish dates | ||
85 | 1.. => Ok(versions[0].id.to_string()), | ||
86 | _ => panic!("available_versions should never be negative"), | ||
87 | } | ||
88 | } | ||
diff --git a/src/commands/update.rs b/src/commands/update.rs index 14c37ec..6275bce 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs | |||
@@ -1,40 +1,138 @@ | |||
1 | use std::io::{Error, ErrorKind}; | 1 | use std::{io::{Error, ErrorKind, Write}, fs::File}; |
2 | 2 | ||
3 | use crate::{config::Cfg, modrinth::projects, get_current_list, db::{get_mods_from_list, get_versions}}; | 3 | use reqwest::Client; |
4 | |||
5 | use futures_util::StreamExt; | ||
6 | |||
7 | use crate::{config::Cfg, modrinth::{projects, Project, versions, extract_current_version, Version}, get_current_list, db::{get_mods_from_list, get_versions, get_list_version, change_list_versions}, List}; | ||
4 | 8 | ||
5 | pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { | 9 | pub async fn update(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { |
6 | 10 | ||
7 | let current_list = get_current_list(config.clone())?; | 11 | let current_list = get_current_list(config.clone())?; |
8 | 12 | ||
9 | let mods = get_mods_from_list(config.clone(), current_list)?; | 13 | let mods = get_mods_from_list(config.clone(), current_list.clone())?; |
14 | |||
15 | let mut versions = get_versions(config.clone(), mods.clone())?; | ||
16 | versions.sort_by_key(|ver| ver.mod_id.clone()); | ||
10 | 17 | ||
11 | let mut projects = projects(String::from(&config.apis.modrinth), mods.clone()).await; | 18 | let mut projects = projects(String::from(&config.apis.modrinth), mods).await; |
19 | projects.sort_by_key(|pro| pro.id.clone()); | ||
12 | 20 | ||
13 | let mut versions = get_versions(config, mods)?; | 21 | let mut updatestack: Vec<Version> = vec![]; |
22 | for (index, project) in projects.into_iter().enumerate() { | ||
23 | let current_version = &versions[index]; | ||
24 | |||
25 | let p_id = String::from(&project.id); | ||
26 | let v_id = ¤t_version.mod_id; | ||
27 | |||
28 | if &p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "SORTING_ERROR"))) }; | ||
29 | |||
30 | if project.versions.join("|") != current_version.versions { | ||
31 | updatestack.push(match specific_update(config.clone(), current_list.clone(), project).await { | ||
32 | Ok(ver) => ver, | ||
33 | //TODO handle errors (only continue on "NO_UPDATE_AVAILABLE") | ||
34 | Err(_) => { continue; }, | ||
35 | }); | ||
36 | }; | ||
37 | }; | ||
38 | //println!("{:?}", updatestack); | ||
39 | |||
40 | //download_updates(config, updatestack).await?; | ||
41 | |||
42 | Ok(()) | ||
43 | } | ||
44 | |||
45 | async fn specific_update(config: Cfg, list: List, project: Project) -> Result<Version, Box<dyn std::error::Error>> { | ||
46 | print!("Checking update for '{}' in {}", project.title, list.id); | ||
14 | 47 | ||
15 | projects.sort_by_key(|p| p.id.clone()); | 48 | let applicable_versions = versions(String::from(&config.apis.modrinth), String::from(&project.id), list.clone()).await; |
49 | |||
50 | let mut versions: Vec<String> = vec![]; | ||
16 | 51 | ||
17 | versions.sort_by_key(|v| v.mod_id.clone()); | 52 | for ver in &applicable_versions { |
53 | versions.push(String::from(&ver.id)); | ||
54 | } | ||
18 | 55 | ||
19 | let mut update_stack: Vec<String> = vec![]; | 56 | let mut current: Vec<Version> = vec![]; |
57 | if versions.join("|") != get_list_version(config.clone(), list.clone(), String::from(&project.id))? { | ||
58 | //get new versions | ||
59 | print!(" | getting new version"); | ||
60 | let current_str = extract_current_version(applicable_versions.clone())?; | ||
61 | current.push(applicable_versions.into_iter().find(|ver| ver.id == current_str).unwrap()); | ||
62 | change_list_versions(config, list, current_str, versions, project.id)?; | ||
63 | } | ||
20 | 64 | ||
21 | for (index, project) in projects.iter().enumerate() { | 65 | if current.is_empty() { return Err(Box::new(Error::new(ErrorKind::NotFound, "NO_UPDATE_AVAILABLE"))) }; |
66 | |||
67 | println!(" | ✔️"); | ||
68 | Ok(current[0].clone()) | ||
69 | } | ||
22 | 70 | ||
23 | let cmp_version = &versions[index]; | 71 | async fn download_updates(config: Cfg, versions: Vec<Version>) -> Result<String, Box<dyn std::error::Error>> { |
24 | 72 | ||
25 | let p_id = &project.id; | 73 | let dl_path = String::from(&config.downloads); |
26 | let v_id = &cmp_version.mod_id; | ||
27 | 74 | ||
28 | if p_id != v_id { return Err(Box::new(Error::new(ErrorKind::Other, "COMPARE_SORTING_ERR"))); }; | 75 | for ver in versions { |
29 | println!("{}:{}", p_id, v_id); | 76 | let primary_file = ver.files.into_iter().find(|file| file.primary).unwrap(); |
77 | let dl_path_file = format!("{}/{}", config.downloads, primary_file.filename); | ||
78 | println!("Downloading {}", primary_file.url); | ||
30 | 79 | ||
31 | if project.versions.join("|") != cmp_version.versions { | 80 | let res = Client::new() |
32 | update_stack.push(String::from(&project.id)); | 81 | .get(String::from(&primary_file.url)) |
33 | }; | 82 | .send() |
34 | }; | 83 | .await |
84 | .or(Err(format!("Failed to GET from '{}'", &primary_file.url)))?; | ||
85 | |||
86 | // download chunks | ||
87 | let mut file = File::create(String::from(&dl_path_file)).or(Err(format!("Failed to create file '{}'", dl_path_file)))?; | ||
88 | let mut stream = res.bytes_stream(); | ||
35 | 89 | ||
36 | //TODO UPDATE | 90 | while let Some(item) = stream.next().await { |
37 | dbg!(update_stack); | 91 | let chunk = item.or(Err("Error while downloading file"))?; |
92 | file.write_all(&chunk) | ||
93 | .or(Err("Error while writing to file"))?; | ||
94 | } | ||
95 | } | ||
38 | 96 | ||
39 | Ok(()) | 97 | Ok(dl_path) |
98 | } | ||
99 | |||
100 | #[tokio::test] | ||
101 | async fn download_updates_test() { | ||
102 | |||
103 | use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, config::{Cfg, Apis}}; | ||
104 | |||
105 | let config = Cfg { data: "...".to_string(), clean_remove: false, downloads: "./dl".to_string(), apis: Apis { modrinth: "...".to_string() } }; | ||
106 | |||
107 | let versions = vec![Version { | ||
108 | id: "dEqtGnT9".to_string(), | ||
109 | project_id: "kYuIpRLv".to_string(), | ||
110 | author_id: "Qnt13hO8".to_string(), | ||
111 | featured: true, | ||
112 | name: "1.2.2-1.19 - Fabric".to_string(), | ||
113 | version_number: "1.2.2-1.19".to_string(), | ||
114 | changelog: None, | ||
115 | date_published: "2022-11-02T17:41:43.072267Z".to_string(), | ||
116 | downloads: 58, | ||
117 | version_type: VersionType::release, | ||
118 | files: vec![VersionFile { | ||
119 | hashes: Hash { | ||
120 | sha1: "fdc6dc39427fc92cc1d7ad8b275b5b83325e712b".to_string(), | ||
121 | sha512: "5b372f00d6e5d6a5ef225c3897826b9f6a2be5506905f7f71b9e939779765b41be6f2a9b029cfc752ad0751d0d2d5f8bb4544408df1363eebdde15641e99a849".to_string() | ||
122 | }, | ||
123 | url: "https://cdn.modrinth.com/data/kYuIpRLv/versions/dEqtGnT9/waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(), | ||
124 | filename: "waveycapes-fabric-1.2.2-mc1.19.2.jar".to_string(), | ||
125 | primary: true, | ||
126 | size: 323176 | ||
127 | }], | ||
128 | game_versions: vec![ | ||
129 | "1.19".to_string(), | ||
130 | "1.19.1".to_string(), | ||
131 | "1.19.2".to_string() | ||
132 | ], | ||
133 | loaders: vec![ | ||
134 | "fabric".to_string() | ||
135 | ] | ||
136 | }]; | ||
137 | assert_eq!(download_updates(config, versions).await.unwrap(), "./dl") | ||
40 | } | 138 | } |
diff --git a/src/config.rs b/src/config.rs index 58d399a..ad59963 100644 --- a/src/config.rs +++ b/src/config.rs | |||
@@ -4,6 +4,7 @@ use serde::Deserialize; | |||
4 | #[derive(Debug, Clone, Deserialize)] | 4 | #[derive(Debug, Clone, Deserialize)] |
5 | pub struct Cfg { | 5 | pub struct Cfg { |
6 | pub data: String, | 6 | pub data: String, |
7 | pub downloads: String, | ||
7 | pub clean_remove: bool, | 8 | pub clean_remove: bool, |
8 | pub apis: Apis, | 9 | pub apis: Apis, |
9 | } | 10 | } |
@@ -2,7 +2,7 @@ use std::io::ErrorKind; | |||
2 | 2 | ||
3 | use crate::{Modloader, config::Cfg, List, modrinth::Version, get_modloader}; | 3 | use crate::{Modloader, config::Cfg, List, modrinth::Version, get_modloader}; |
4 | 4 | ||
5 | //TODO use prepared statements | 5 | //TODO use prepared statements / change to rusqlite |
6 | 6 | ||
7 | //MODS | 7 | //MODS |
8 | pub fn insert_mod(config: Cfg, id: String, name: String, versions: Vec<String>) -> Result<(), sqlite::Error> { | 8 | pub fn insert_mod(config: Cfg, id: String, name: String, versions: Vec<String>) -> Result<(), sqlite::Error> { |
@@ -151,6 +151,27 @@ pub fn get_versions(config: Cfg, mods: Vec<String>) -> Result<Vec<DBModlistVersi | |||
151 | Ok(versionmaps) | 151 | Ok(versionmaps) |
152 | } | 152 | } |
153 | 153 | ||
154 | pub fn get_list_version(config: Cfg, list: List, mod_id: String) -> Result<String, Box<dyn std::error::Error>> { | ||
155 | let data = format!("{}/data.db", config.data); | ||
156 | let connection = sqlite::open(data).unwrap(); | ||
157 | |||
158 | let sql = format!("SELECT applicable_versions FROM {} WHERE mod_id = '{}'", list.id, mod_id); | ||
159 | |||
160 | //TODO catch sql errors better | ||
161 | let mut version: String = String::new(); | ||
162 | connection.iterate(sql, |ver| { | ||
163 | if ver.is_empty() { return false; }; | ||
164 | for &(_column, value) in ver.iter() { | ||
165 | version = String::from(value.unwrap()); | ||
166 | } | ||
167 | true | ||
168 | }).unwrap(); | ||
169 | |||
170 | if version.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; | ||
171 | |||
172 | Ok(version) | ||
173 | } | ||
174 | |||
154 | 175 | ||
155 | //LIST | 176 | //LIST |
156 | pub fn insert_list(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), sqlite::Error> { | 177 | pub fn insert_list(config: Cfg, id: String, mc_version: String, mod_loader: Modloader) -> Result<(), sqlite::Error> { |
@@ -217,6 +238,15 @@ pub fn get_list(config: Cfg, id: String) -> Result<List, Box<dyn std::error::Err | |||
217 | Ok(List { id, mc_version: String::from(&list[0]), modloader: get_modloader(String::from(&list[1]))? }) | 238 | Ok(List { id, mc_version: String::from(&list[0]), modloader: get_modloader(String::from(&list[1]))? }) |
218 | } | 239 | } |
219 | 240 | ||
241 | pub fn change_list_versions(config: Cfg, list: List, current_version: String, versions: Vec<String>, mod_id: String) -> Result<(), sqlite::Error> { | ||
242 | let data = format!("{}/data.db", config.data); | ||
243 | let connection = sqlite::open(data).unwrap(); | ||
244 | |||
245 | let sql = format!("UPDATE {} SET current_version = '{}', applicable_versions = '{}' WHERE mod_id = '{}'", list.id, current_version, versions.join("|"), mod_id); | ||
246 | |||
247 | connection.execute(sql) | ||
248 | } | ||
249 | |||
220 | //config | 250 | //config |
221 | pub fn change_list(config: Cfg, id: String) -> Result<(), sqlite::Error> { | 251 | pub fn change_list(config: Cfg, id: String) -> Result<(), sqlite::Error> { |
222 | let data = format!("{}/data.db", config.data); | 252 | let data = format!("{}/data.db", config.data); |
diff --git a/src/input.rs b/src/input.rs index 0c13e67..e0c9ae9 100644 --- a/src/input.rs +++ b/src/input.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use std::io::{stdin, Error, ErrorKind}; | 1 | use std::io::{stdin, Error, ErrorKind}; |
2 | use crate::{config::Cfg, list, modification, update}; | 2 | use crate::{config::Cfg, list, modification, update}; |
3 | 3 | ||
4 | #[derive(Debug, PartialEq, Eq)] | ||
4 | pub struct Input { | 5 | pub struct Input { |
5 | pub command: String, | 6 | pub command: String, |
6 | pub args: Option<Vec<String>>, | 7 | pub args: Option<Vec<String>>, |
@@ -27,8 +28,6 @@ impl Input { | |||
27 | }, | 28 | }, |
28 | _ => { panic!("This should never happen") } | 29 | _ => { panic!("This should never happen") } |
29 | } | 30 | } |
30 | |||
31 | |||
32 | } | 31 | } |
33 | } | 32 | } |
34 | 33 | ||
@@ -53,3 +52,10 @@ pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> { | |||
53 | _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))), | 52 | _ => Err(Box::new(Error::new(ErrorKind::InvalidInput, "UNKNOWN_COMMAND"))), |
54 | } | 53 | } |
55 | } | 54 | } |
55 | |||
56 | #[test] | ||
57 | fn input_from() { | ||
58 | let string = String::from("list add test 1.19.2 fabric"); | ||
59 | let input = Input { command: String::from("list"), args: Some(vec![String::from("add"), String::from("test"), String::from("1.19.2"), String::from("fabric")]) }; | ||
60 | assert_eq!(Input::from(string).unwrap(), input); | ||
61 | } | ||
diff --git a/tests/db/mod.rs b/tests/db/mod.rs index b5aed75..50e6887 100644 --- a/tests/db/mod.rs +++ b/tests/db/mod.rs | |||
@@ -1,5 +1,3 @@ | |||
1 | use std::fs::File; | ||
2 | |||
3 | pub fn setup() { | 1 | pub fn setup() { |
4 | File::create("./setuptests").unwrap(); | 2 | //File::create("./setuptests").unwrap(); |
5 | } | 3 | } |
diff --git a/tests/db_integration.rs b/tests/db_integration.rs index 82cfe0f..f580595 100644 --- a/tests/db_integration.rs +++ b/tests/db_integration.rs | |||
@@ -3,5 +3,4 @@ mod db; | |||
3 | #[test] | 3 | #[test] |
4 | fn test_add() { | 4 | fn test_add() { |
5 | db::setup(); | 5 | db::setup(); |
6 | assert!(true); | ||
7 | } | 6 | } |