summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock633
-rw-r--r--Cargo.toml8
-rw-r--r--src/apis/modrinth.rs56
-rw-r--r--src/commands/download.rs46
-rw-r--r--src/commands/io.rs78
-rw-r--r--src/commands/list.rs81
-rw-r--r--src/commands/mod.rs16
-rw-r--r--src/commands/modification.rs223
-rw-r--r--src/commands/setup.rs12
-rw-r--r--src/commands/update.rs132
-rw-r--r--src/config.rs18
-rw-r--r--src/db.rs431
-rw-r--r--src/error.rs37
-rw-r--r--src/files.rs59
-rw-r--r--src/input.rs344
-rw-r--r--src/lib.rs29
-rw-r--r--src/main.rs312
17 files changed, 1522 insertions, 993 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fc25ffa..f99cd87 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,6 +38,55 @@ dependencies = [
38] 38]
39 39
40[[package]] 40[[package]]
41name = "anstream"
42version = "0.3.0"
43source = "registry+https://github.com/rust-lang/crates.io-index"
44checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371"
45dependencies = [
46 "anstyle",
47 "anstyle-parse",
48 "anstyle-query",
49 "anstyle-wincon",
50 "colorchoice",
51 "is-terminal",
52 "utf8parse",
53]
54
55[[package]]
56name = "anstyle"
57version = "1.0.0"
58source = "registry+https://github.com/rust-lang/crates.io-index"
59checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
60
61[[package]]
62name = "anstyle-parse"
63version = "0.2.0"
64source = "registry+https://github.com/rust-lang/crates.io-index"
65checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
66dependencies = [
67 "utf8parse",
68]
69
70[[package]]
71name = "anstyle-query"
72version = "1.0.0"
73source = "registry+https://github.com/rust-lang/crates.io-index"
74checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
75dependencies = [
76 "windows-sys 0.48.0",
77]
78
79[[package]]
80name = "anstyle-wincon"
81version = "1.0.0"
82source = "registry+https://github.com/rust-lang/crates.io-index"
83checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd"
84dependencies = [
85 "anstyle",
86 "windows-sys 0.48.0",
87]
88
89[[package]]
41name = "autocfg" 90name = "autocfg"
42version = "1.1.0" 91version = "1.1.0"
43source = "registry+https://github.com/rust-lang/crates.io-index" 92source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -71,6 +120,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
71checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 120checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
72 121
73[[package]] 122[[package]]
123name = "bitflags"
124version = "2.0.2"
125source = "registry+https://github.com/rust-lang/crates.io-index"
126checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
127
128[[package]]
74name = "bumpalo" 129name = "bumpalo"
75version = "3.12.0" 130version = "3.12.0"
76source = "registry+https://github.com/rust-lang/crates.io-index" 131source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -96,9 +151,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
96 151
97[[package]] 152[[package]]
98name = "chrono" 153name = "chrono"
99version = "0.4.23" 154version = "0.4.24"
100source = "registry+https://github.com/rust-lang/crates.io-index" 155source = "registry+https://github.com/rust-lang/crates.io-index"
101checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" 156checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
102dependencies = [ 157dependencies = [
103 "iana-time-zone", 158 "iana-time-zone",
104 "js-sys", 159 "js-sys",
@@ -110,6 +165,57 @@ dependencies = [
110] 165]
111 166
112[[package]] 167[[package]]
168name = "clap"
169version = "4.2.2"
170source = "registry+https://github.com/rust-lang/crates.io-index"
171checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a"
172dependencies = [
173 "clap_builder",
174 "clap_derive",
175 "once_cell",
176]
177
178[[package]]
179name = "clap_builder"
180version = "4.2.2"
181source = "registry+https://github.com/rust-lang/crates.io-index"
182checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6"
183dependencies = [
184 "anstream",
185 "anstyle",
186 "bitflags 1.3.2",
187 "clap_lex",
188 "strsim",
189]
190
191[[package]]
192name = "clap_complete"
193version = "4.2.0"
194source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd"
196dependencies = [
197 "clap",
198]
199
200[[package]]
201name = "clap_derive"
202version = "4.2.0"
203source = "registry+https://github.com/rust-lang/crates.io-index"
204checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
205dependencies = [
206 "heck",
207 "proc-macro2",
208 "quote",
209 "syn 2.0.13",
210]
211
212[[package]]
213name = "clap_lex"
214version = "0.4.1"
215source = "registry+https://github.com/rust-lang/crates.io-index"
216checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
217
218[[package]]
113name = "codespan-reporting" 219name = "codespan-reporting"
114version = "0.11.1" 220version = "0.11.1"
115source = "registry+https://github.com/rust-lang/crates.io-index" 221source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -120,6 +226,12 @@ dependencies = [
120] 226]
121 227
122[[package]] 228[[package]]
229name = "colorchoice"
230version = "1.0.0"
231source = "registry+https://github.com/rust-lang/crates.io-index"
232checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
233
234[[package]]
123name = "core-foundation" 235name = "core-foundation"
124version = "0.9.3" 236version = "0.9.3"
125source = "registry+https://github.com/rust-lang/crates.io-index" 237source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -131,15 +243,15 @@ dependencies = [
131 243
132[[package]] 244[[package]]
133name = "core-foundation-sys" 245name = "core-foundation-sys"
134version = "0.8.3" 246version = "0.8.4"
135source = "registry+https://github.com/rust-lang/crates.io-index" 247source = "registry+https://github.com/rust-lang/crates.io-index"
136checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 248checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
137 249
138[[package]] 250[[package]]
139name = "cxx" 251name = "cxx"
140version = "1.0.91" 252version = "1.0.94"
141source = "registry+https://github.com/rust-lang/crates.io-index" 253source = "registry+https://github.com/rust-lang/crates.io-index"
142checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" 254checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
143dependencies = [ 255dependencies = [
144 "cc", 256 "cc",
145 "cxxbridge-flags", 257 "cxxbridge-flags",
@@ -149,9 +261,9 @@ dependencies = [
149 261
150[[package]] 262[[package]]
151name = "cxx-build" 263name = "cxx-build"
152version = "1.0.91" 264version = "1.0.94"
153source = "registry+https://github.com/rust-lang/crates.io-index" 265source = "registry+https://github.com/rust-lang/crates.io-index"
154checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" 266checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
155dependencies = [ 267dependencies = [
156 "cc", 268 "cc",
157 "codespan-reporting", 269 "codespan-reporting",
@@ -159,44 +271,44 @@ dependencies = [
159 "proc-macro2", 271 "proc-macro2",
160 "quote", 272 "quote",
161 "scratch", 273 "scratch",
162 "syn", 274 "syn 2.0.13",
163] 275]
164 276
165[[package]] 277[[package]]
166name = "cxxbridge-flags" 278name = "cxxbridge-flags"
167version = "1.0.91" 279version = "1.0.94"
168source = "registry+https://github.com/rust-lang/crates.io-index" 280source = "registry+https://github.com/rust-lang/crates.io-index"
169checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" 281checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
170 282
171[[package]] 283[[package]]
172name = "cxxbridge-macro" 284name = "cxxbridge-macro"
173version = "1.0.91" 285version = "1.0.94"
174source = "registry+https://github.com/rust-lang/crates.io-index" 286source = "registry+https://github.com/rust-lang/crates.io-index"
175checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" 287checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
176dependencies = [ 288dependencies = [
177 "proc-macro2", 289 "proc-macro2",
178 "quote", 290 "quote",
179 "syn", 291 "syn 2.0.13",
180] 292]
181 293
182[[package]] 294[[package]]
183name = "dirs" 295name = "dirs"
184version = "4.0.0" 296version = "5.0.0"
185source = "registry+https://github.com/rust-lang/crates.io-index" 297source = "registry+https://github.com/rust-lang/crates.io-index"
186checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 298checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd"
187dependencies = [ 299dependencies = [
188 "dirs-sys", 300 "dirs-sys",
189] 301]
190 302
191[[package]] 303[[package]]
192name = "dirs-sys" 304name = "dirs-sys"
193version = "0.3.7" 305version = "0.4.0"
194source = "registry+https://github.com/rust-lang/crates.io-index" 306source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 307checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b"
196dependencies = [ 308dependencies = [
197 "libc", 309 "libc",
198 "redox_users", 310 "redox_users",
199 "winapi", 311 "windows-sys 0.45.0",
200] 312]
201 313
202[[package]] 314[[package]]
@@ -209,6 +321,27 @@ dependencies = [
209] 321]
210 322
211[[package]] 323[[package]]
324name = "errno"
325version = "0.3.0"
326source = "registry+https://github.com/rust-lang/crates.io-index"
327checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
328dependencies = [
329 "errno-dragonfly",
330 "libc",
331 "windows-sys 0.45.0",
332]
333
334[[package]]
335name = "errno-dragonfly"
336version = "0.1.2"
337source = "registry+https://github.com/rust-lang/crates.io-index"
338checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
339dependencies = [
340 "cc",
341 "libc",
342]
343
344[[package]]
212name = "error-chain" 345name = "error-chain"
213version = "0.12.4" 346version = "0.12.4"
214source = "registry+https://github.com/rust-lang/crates.io-index" 347source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -271,53 +404,53 @@ dependencies = [
271 404
272[[package]] 405[[package]]
273name = "futures-channel" 406name = "futures-channel"
274version = "0.3.26" 407version = "0.3.28"
275source = "registry+https://github.com/rust-lang/crates.io-index" 408source = "registry+https://github.com/rust-lang/crates.io-index"
276checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" 409checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
277dependencies = [ 410dependencies = [
278 "futures-core", 411 "futures-core",
279] 412]
280 413
281[[package]] 414[[package]]
282name = "futures-core" 415name = "futures-core"
283version = "0.3.26" 416version = "0.3.28"
284source = "registry+https://github.com/rust-lang/crates.io-index" 417source = "registry+https://github.com/rust-lang/crates.io-index"
285checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" 418checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
286 419
287[[package]] 420[[package]]
288name = "futures-io" 421name = "futures-io"
289version = "0.3.26" 422version = "0.3.28"
290source = "registry+https://github.com/rust-lang/crates.io-index" 423source = "registry+https://github.com/rust-lang/crates.io-index"
291checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" 424checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
292 425
293[[package]] 426[[package]]
294name = "futures-macro" 427name = "futures-macro"
295version = "0.3.26" 428version = "0.3.28"
296source = "registry+https://github.com/rust-lang/crates.io-index" 429source = "registry+https://github.com/rust-lang/crates.io-index"
297checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" 430checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
298dependencies = [ 431dependencies = [
299 "proc-macro2", 432 "proc-macro2",
300 "quote", 433 "quote",
301 "syn", 434 "syn 2.0.13",
302] 435]
303 436
304[[package]] 437[[package]]
305name = "futures-sink" 438name = "futures-sink"
306version = "0.3.26" 439version = "0.3.28"
307source = "registry+https://github.com/rust-lang/crates.io-index" 440source = "registry+https://github.com/rust-lang/crates.io-index"
308checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" 441checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
309 442
310[[package]] 443[[package]]
311name = "futures-task" 444name = "futures-task"
312version = "0.3.26" 445version = "0.3.28"
313source = "registry+https://github.com/rust-lang/crates.io-index" 446source = "registry+https://github.com/rust-lang/crates.io-index"
314checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" 447checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
315 448
316[[package]] 449[[package]]
317name = "futures-util" 450name = "futures-util"
318version = "0.3.26" 451version = "0.3.28"
319source = "registry+https://github.com/rust-lang/crates.io-index" 452source = "registry+https://github.com/rust-lang/crates.io-index"
320checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" 453checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
321dependencies = [ 454dependencies = [
322 "futures-core", 455 "futures-core",
323 "futures-io", 456 "futures-io",
@@ -349,9 +482,9 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
349 482
350[[package]] 483[[package]]
351name = "h2" 484name = "h2"
352version = "0.3.15" 485version = "0.3.16"
353source = "registry+https://github.com/rust-lang/crates.io-index" 486source = "registry+https://github.com/rust-lang/crates.io-index"
354checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" 487checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
355dependencies = [ 488dependencies = [
356 "bytes", 489 "bytes",
357 "fnv", 490 "fnv",
@@ -385,6 +518,12 @@ dependencies = [
385] 518]
386 519
387[[package]] 520[[package]]
521name = "heck"
522version = "0.4.1"
523source = "registry+https://github.com/rust-lang/crates.io-index"
524checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
525
526[[package]]
388name = "hermit-abi" 527name = "hermit-abi"
389version = "0.2.6" 528version = "0.2.6"
390source = "registry+https://github.com/rust-lang/crates.io-index" 529source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -394,6 +533,12 @@ dependencies = [
394] 533]
395 534
396[[package]] 535[[package]]
536name = "hermit-abi"
537version = "0.3.1"
538source = "registry+https://github.com/rust-lang/crates.io-index"
539checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
540
541[[package]]
397name = "http" 542name = "http"
398version = "0.2.9" 543version = "0.2.9"
399source = "registry+https://github.com/rust-lang/crates.io-index" 544source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -429,9 +574,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
429 574
430[[package]] 575[[package]]
431name = "hyper" 576name = "hyper"
432version = "0.14.24" 577version = "0.14.25"
433source = "registry+https://github.com/rust-lang/crates.io-index" 578source = "registry+https://github.com/rust-lang/crates.io-index"
434checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" 579checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
435dependencies = [ 580dependencies = [
436 "bytes", 581 "bytes",
437 "futures-channel", 582 "futures-channel",
@@ -466,16 +611,16 @@ dependencies = [
466 611
467[[package]] 612[[package]]
468name = "iana-time-zone" 613name = "iana-time-zone"
469version = "0.1.53" 614version = "0.1.56"
470source = "registry+https://github.com/rust-lang/crates.io-index" 615source = "registry+https://github.com/rust-lang/crates.io-index"
471checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" 616checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
472dependencies = [ 617dependencies = [
473 "android_system_properties", 618 "android_system_properties",
474 "core-foundation-sys", 619 "core-foundation-sys",
475 "iana-time-zone-haiku", 620 "iana-time-zone-haiku",
476 "js-sys", 621 "js-sys",
477 "wasm-bindgen", 622 "wasm-bindgen",
478 "winapi", 623 "windows",
479] 624]
480 625
481[[package]] 626[[package]]
@@ -500,9 +645,9 @@ dependencies = [
500 645
501[[package]] 646[[package]]
502name = "indexmap" 647name = "indexmap"
503version = "1.9.2" 648version = "1.9.3"
504source = "registry+https://github.com/rust-lang/crates.io-index" 649source = "registry+https://github.com/rust-lang/crates.io-index"
505checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 650checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
506dependencies = [ 651dependencies = [
507 "autocfg", 652 "autocfg",
508 "hashbrown", 653 "hashbrown",
@@ -518,16 +663,39 @@ dependencies = [
518] 663]
519 664
520[[package]] 665[[package]]
666name = "io-lifetimes"
667version = "1.0.9"
668source = "registry+https://github.com/rust-lang/crates.io-index"
669checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
670dependencies = [
671 "hermit-abi 0.3.1",
672 "libc",
673 "windows-sys 0.45.0",
674]
675
676[[package]]
521name = "ipnet" 677name = "ipnet"
522version = "2.7.1" 678version = "2.7.2"
679source = "registry+https://github.com/rust-lang/crates.io-index"
680checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
681
682[[package]]
683name = "is-terminal"
684version = "0.4.6"
523source = "registry+https://github.com/rust-lang/crates.io-index" 685source = "registry+https://github.com/rust-lang/crates.io-index"
524checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" 686checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
687dependencies = [
688 "hermit-abi 0.3.1",
689 "io-lifetimes",
690 "rustix",
691 "windows-sys 0.45.0",
692]
525 693
526[[package]] 694[[package]]
527name = "itoa" 695name = "itoa"
528version = "1.0.5" 696version = "1.0.6"
529source = "registry+https://github.com/rust-lang/crates.io-index" 697source = "registry+https://github.com/rust-lang/crates.io-index"
530checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" 698checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
531 699
532[[package]] 700[[package]]
533name = "js-sys" 701name = "js-sys"
@@ -546,15 +714,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
546 714
547[[package]] 715[[package]]
548name = "libc" 716name = "libc"
549version = "0.2.139" 717version = "0.2.141"
550source = "registry+https://github.com/rust-lang/crates.io-index" 718source = "registry+https://github.com/rust-lang/crates.io-index"
551checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 719checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
552 720
553[[package]] 721[[package]]
554name = "libsqlite3-sys" 722name = "libsqlite3-sys"
555version = "0.25.2" 723version = "0.26.0"
556source = "registry+https://github.com/rust-lang/crates.io-index" 724source = "registry+https://github.com/rust-lang/crates.io-index"
557checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa" 725checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
558dependencies = [ 726dependencies = [
559 "cc", 727 "cc",
560 "pkg-config", 728 "pkg-config",
@@ -571,6 +739,12 @@ dependencies = [
571] 739]
572 740
573[[package]] 741[[package]]
742name = "linux-raw-sys"
743version = "0.3.1"
744source = "registry+https://github.com/rust-lang/crates.io-index"
745checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
746
747[[package]]
574name = "lock_api" 748name = "lock_api"
575version = "0.4.9" 749version = "0.4.9"
576source = "registry+https://github.com/rust-lang/crates.io-index" 750source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -597,9 +771,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
597 771
598[[package]] 772[[package]]
599name = "mime" 773name = "mime"
600version = "0.3.16" 774version = "0.3.17"
601source = "registry+https://github.com/rust-lang/crates.io-index" 775source = "registry+https://github.com/rust-lang/crates.io-index"
602checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 776checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
603 777
604[[package]] 778[[package]]
605name = "miniz_oxide" 779name = "miniz_oxide"
@@ -624,9 +798,11 @@ dependencies = [
624 798
625[[package]] 799[[package]]
626name = "modlist" 800name = "modlist"
627version = "0.11.0" 801version = "0.12.0"
628dependencies = [ 802dependencies = [
629 "chrono", 803 "chrono",
804 "clap",
805 "clap_complete",
630 "dirs", 806 "dirs",
631 "error-chain", 807 "error-chain",
632 "futures-util", 808 "futures-util",
@@ -657,15 +833,6 @@ dependencies = [
657] 833]
658 834
659[[package]] 835[[package]]
660name = "nom8"
661version = "0.2.0"
662source = "registry+https://github.com/rust-lang/crates.io-index"
663checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
664dependencies = [
665 "memchr",
666]
667
668[[package]]
669name = "num-integer" 836name = "num-integer"
670version = "0.1.45" 837version = "0.1.45"
671source = "registry+https://github.com/rust-lang/crates.io-index" 838source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -690,7 +857,7 @@ version = "1.15.0"
690source = "registry+https://github.com/rust-lang/crates.io-index" 857source = "registry+https://github.com/rust-lang/crates.io-index"
691checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 858checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
692dependencies = [ 859dependencies = [
693 "hermit-abi", 860 "hermit-abi 0.2.6",
694 "libc", 861 "libc",
695] 862]
696 863
@@ -711,11 +878,11 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
711 878
712[[package]] 879[[package]]
713name = "openssl" 880name = "openssl"
714version = "0.10.45" 881version = "0.10.49"
715source = "registry+https://github.com/rust-lang/crates.io-index" 882source = "registry+https://github.com/rust-lang/crates.io-index"
716checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" 883checksum = "4d2f106ab837a24e03672c59b1239669a0596406ff657c3c0835b6b7f0f35a33"
717dependencies = [ 884dependencies = [
718 "bitflags", 885 "bitflags 1.3.2",
719 "cfg-if", 886 "cfg-if",
720 "foreign-types", 887 "foreign-types",
721 "libc", 888 "libc",
@@ -726,13 +893,13 @@ dependencies = [
726 893
727[[package]] 894[[package]]
728name = "openssl-macros" 895name = "openssl-macros"
729version = "0.1.0" 896version = "0.1.1"
730source = "registry+https://github.com/rust-lang/crates.io-index" 897source = "registry+https://github.com/rust-lang/crates.io-index"
731checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 898checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
732dependencies = [ 899dependencies = [
733 "proc-macro2", 900 "proc-macro2",
734 "quote", 901 "quote",
735 "syn", 902 "syn 2.0.13",
736] 903]
737 904
738[[package]] 905[[package]]
@@ -743,11 +910,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
743 910
744[[package]] 911[[package]]
745name = "openssl-sys" 912name = "openssl-sys"
746version = "0.9.80" 913version = "0.9.84"
747source = "registry+https://github.com/rust-lang/crates.io-index" 914source = "registry+https://github.com/rust-lang/crates.io-index"
748checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" 915checksum = "3a20eace9dc2d82904039cb76dcf50fb1a0bba071cfd1629720b5d6f1ddba0fa"
749dependencies = [ 916dependencies = [
750 "autocfg",
751 "cc", 917 "cc",
752 "libc", 918 "libc",
753 "pkg-config", 919 "pkg-config",
@@ -772,7 +938,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
772dependencies = [ 938dependencies = [
773 "cfg-if", 939 "cfg-if",
774 "libc", 940 "libc",
775 "redox_syscall", 941 "redox_syscall 0.2.16",
776 "smallvec", 942 "smallvec",
777 "windows-sys 0.45.0", 943 "windows-sys 0.45.0",
778] 944]
@@ -803,18 +969,18 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
803 969
804[[package]] 970[[package]]
805name = "proc-macro2" 971name = "proc-macro2"
806version = "1.0.51" 972version = "1.0.56"
807source = "registry+https://github.com/rust-lang/crates.io-index" 973source = "registry+https://github.com/rust-lang/crates.io-index"
808checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 974checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
809dependencies = [ 975dependencies = [
810 "unicode-ident", 976 "unicode-ident",
811] 977]
812 978
813[[package]] 979[[package]]
814name = "quote" 980name = "quote"
815version = "1.0.23" 981version = "1.0.26"
816source = "registry+https://github.com/rust-lang/crates.io-index" 982source = "registry+https://github.com/rust-lang/crates.io-index"
817checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 983checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
818dependencies = [ 984dependencies = [
819 "proc-macro2", 985 "proc-macro2",
820] 986]
@@ -825,34 +991,34 @@ version = "0.2.16"
825source = "registry+https://github.com/rust-lang/crates.io-index" 991source = "registry+https://github.com/rust-lang/crates.io-index"
826checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 992checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
827dependencies = [ 993dependencies = [
828 "bitflags", 994 "bitflags 1.3.2",
829] 995]
830 996
831[[package]] 997[[package]]
832name = "redox_users" 998name = "redox_syscall"
833version = "0.4.3" 999version = "0.3.5"
834source = "registry+https://github.com/rust-lang/crates.io-index" 1000source = "registry+https://github.com/rust-lang/crates.io-index"
835checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" 1001checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
836dependencies = [ 1002dependencies = [
837 "getrandom", 1003 "bitflags 1.3.2",
838 "redox_syscall",
839 "thiserror",
840] 1004]
841 1005
842[[package]] 1006[[package]]
843name = "remove_dir_all" 1007name = "redox_users"
844version = "0.5.3" 1008version = "0.4.3"
845source = "registry+https://github.com/rust-lang/crates.io-index" 1009source = "registry+https://github.com/rust-lang/crates.io-index"
846checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1010checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
847dependencies = [ 1011dependencies = [
848 "winapi", 1012 "getrandom",
1013 "redox_syscall 0.2.16",
1014 "thiserror",
849] 1015]
850 1016
851[[package]] 1017[[package]]
852name = "reqwest" 1018name = "reqwest"
853version = "0.11.14" 1019version = "0.11.16"
854source = "registry+https://github.com/rust-lang/crates.io-index" 1020source = "registry+https://github.com/rust-lang/crates.io-index"
855checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" 1021checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254"
856dependencies = [ 1022dependencies = [
857 "base64", 1023 "base64",
858 "bytes", 1024 "bytes",
@@ -889,11 +1055,11 @@ dependencies = [
889 1055
890[[package]] 1056[[package]]
891name = "rusqlite" 1057name = "rusqlite"
892version = "0.28.0" 1058version = "0.29.0"
893source = "registry+https://github.com/rust-lang/crates.io-index" 1059source = "registry+https://github.com/rust-lang/crates.io-index"
894checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" 1060checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
895dependencies = [ 1061dependencies = [
896 "bitflags", 1062 "bitflags 2.0.2",
897 "fallible-iterator", 1063 "fallible-iterator",
898 "fallible-streaming-iterator", 1064 "fallible-streaming-iterator",
899 "hashlink", 1065 "hashlink",
@@ -903,15 +1069,29 @@ dependencies = [
903 1069
904[[package]] 1070[[package]]
905name = "rustc-demangle" 1071name = "rustc-demangle"
906version = "0.1.21" 1072version = "0.1.22"
1073source = "registry+https://github.com/rust-lang/crates.io-index"
1074checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b"
1075
1076[[package]]
1077name = "rustix"
1078version = "0.37.7"
907source = "registry+https://github.com/rust-lang/crates.io-index" 1079source = "registry+https://github.com/rust-lang/crates.io-index"
908checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 1080checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
1081dependencies = [
1082 "bitflags 1.3.2",
1083 "errno",
1084 "io-lifetimes",
1085 "libc",
1086 "linux-raw-sys",
1087 "windows-sys 0.45.0",
1088]
909 1089
910[[package]] 1090[[package]]
911name = "ryu" 1091name = "ryu"
912version = "1.0.12" 1092version = "1.0.13"
913source = "registry+https://github.com/rust-lang/crates.io-index" 1093source = "registry+https://github.com/rust-lang/crates.io-index"
914checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" 1094checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
915 1095
916[[package]] 1096[[package]]
917name = "schannel" 1097name = "schannel"
@@ -930,9 +1110,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
930 1110
931[[package]] 1111[[package]]
932name = "scratch" 1112name = "scratch"
933version = "1.0.3" 1113version = "1.0.5"
934source = "registry+https://github.com/rust-lang/crates.io-index" 1114source = "registry+https://github.com/rust-lang/crates.io-index"
935checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" 1115checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
936 1116
937[[package]] 1117[[package]]
938name = "security-framework" 1118name = "security-framework"
@@ -940,7 +1120,7 @@ version = "2.8.2"
940source = "registry+https://github.com/rust-lang/crates.io-index" 1120source = "registry+https://github.com/rust-lang/crates.io-index"
941checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" 1121checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
942dependencies = [ 1122dependencies = [
943 "bitflags", 1123 "bitflags 1.3.2",
944 "core-foundation", 1124 "core-foundation",
945 "core-foundation-sys", 1125 "core-foundation-sys",
946 "libc", 1126 "libc",
@@ -959,29 +1139,29 @@ dependencies = [
959 1139
960[[package]] 1140[[package]]
961name = "serde" 1141name = "serde"
962version = "1.0.152" 1142version = "1.0.159"
963source = "registry+https://github.com/rust-lang/crates.io-index" 1143source = "registry+https://github.com/rust-lang/crates.io-index"
964checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 1144checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
965dependencies = [ 1145dependencies = [
966 "serde_derive", 1146 "serde_derive",
967] 1147]
968 1148
969[[package]] 1149[[package]]
970name = "serde_derive" 1150name = "serde_derive"
971version = "1.0.152" 1151version = "1.0.159"
972source = "registry+https://github.com/rust-lang/crates.io-index" 1152source = "registry+https://github.com/rust-lang/crates.io-index"
973checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 1153checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
974dependencies = [ 1154dependencies = [
975 "proc-macro2", 1155 "proc-macro2",
976 "quote", 1156 "quote",
977 "syn", 1157 "syn 2.0.13",
978] 1158]
979 1159
980[[package]] 1160[[package]]
981name = "serde_json" 1161name = "serde_json"
982version = "1.0.93" 1162version = "1.0.95"
983source = "registry+https://github.com/rust-lang/crates.io-index" 1163source = "registry+https://github.com/rust-lang/crates.io-index"
984checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" 1164checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
985dependencies = [ 1165dependencies = [
986 "itoa", 1166 "itoa",
987 "ryu", 1167 "ryu",
@@ -1020,9 +1200,9 @@ dependencies = [
1020 1200
1021[[package]] 1201[[package]]
1022name = "slab" 1202name = "slab"
1023version = "0.4.7" 1203version = "0.4.8"
1024source = "registry+https://github.com/rust-lang/crates.io-index" 1204source = "registry+https://github.com/rust-lang/crates.io-index"
1025checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 1205checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
1026dependencies = [ 1206dependencies = [
1027 "autocfg", 1207 "autocfg",
1028] 1208]
@@ -1035,19 +1215,36 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
1035 1215
1036[[package]] 1216[[package]]
1037name = "socket2" 1217name = "socket2"
1038version = "0.4.7" 1218version = "0.4.9"
1039source = "registry+https://github.com/rust-lang/crates.io-index" 1219source = "registry+https://github.com/rust-lang/crates.io-index"
1040checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1220checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
1041dependencies = [ 1221dependencies = [
1042 "libc", 1222 "libc",
1043 "winapi", 1223 "winapi",
1044] 1224]
1045 1225
1046[[package]] 1226[[package]]
1227name = "strsim"
1228version = "0.10.0"
1229source = "registry+https://github.com/rust-lang/crates.io-index"
1230checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
1231
1232[[package]]
1233name = "syn"
1234version = "1.0.109"
1235source = "registry+https://github.com/rust-lang/crates.io-index"
1236checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
1237dependencies = [
1238 "proc-macro2",
1239 "quote",
1240 "unicode-ident",
1241]
1242
1243[[package]]
1047name = "syn" 1244name = "syn"
1048version = "1.0.107" 1245version = "2.0.13"
1049source = "registry+https://github.com/rust-lang/crates.io-index" 1246source = "registry+https://github.com/rust-lang/crates.io-index"
1050checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 1247checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
1051dependencies = [ 1248dependencies = [
1052 "proc-macro2", 1249 "proc-macro2",
1053 "quote", 1250 "quote",
@@ -1056,16 +1253,15 @@ dependencies = [
1056 1253
1057[[package]] 1254[[package]]
1058name = "tempfile" 1255name = "tempfile"
1059version = "3.3.0" 1256version = "3.5.0"
1060source = "registry+https://github.com/rust-lang/crates.io-index" 1257source = "registry+https://github.com/rust-lang/crates.io-index"
1061checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1258checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
1062dependencies = [ 1259dependencies = [
1063 "cfg-if", 1260 "cfg-if",
1064 "fastrand", 1261 "fastrand",
1065 "libc", 1262 "redox_syscall 0.3.5",
1066 "redox_syscall", 1263 "rustix",
1067 "remove_dir_all", 1264 "windows-sys 0.45.0",
1068 "winapi",
1069] 1265]
1070 1266
1071[[package]] 1267[[package]]
@@ -1079,22 +1275,22 @@ dependencies = [
1079 1275
1080[[package]] 1276[[package]]
1081name = "thiserror" 1277name = "thiserror"
1082version = "1.0.38" 1278version = "1.0.40"
1083source = "registry+https://github.com/rust-lang/crates.io-index" 1279source = "registry+https://github.com/rust-lang/crates.io-index"
1084checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" 1280checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
1085dependencies = [ 1281dependencies = [
1086 "thiserror-impl", 1282 "thiserror-impl",
1087] 1283]
1088 1284
1089[[package]] 1285[[package]]
1090name = "thiserror-impl" 1286name = "thiserror-impl"
1091version = "1.0.38" 1287version = "1.0.40"
1092source = "registry+https://github.com/rust-lang/crates.io-index" 1288source = "registry+https://github.com/rust-lang/crates.io-index"
1093checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" 1289checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
1094dependencies = [ 1290dependencies = [
1095 "proc-macro2", 1291 "proc-macro2",
1096 "quote", 1292 "quote",
1097 "syn", 1293 "syn 2.0.13",
1098] 1294]
1099 1295
1100[[package]] 1296[[package]]
@@ -1125,14 +1321,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1125 1321
1126[[package]] 1322[[package]]
1127name = "tokio" 1323name = "tokio"
1128version = "1.25.0" 1324version = "1.27.0"
1129source = "registry+https://github.com/rust-lang/crates.io-index" 1325source = "registry+https://github.com/rust-lang/crates.io-index"
1130checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" 1326checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
1131dependencies = [ 1327dependencies = [
1132 "autocfg", 1328 "autocfg",
1133 "bytes", 1329 "bytes",
1134 "libc", 1330 "libc",
1135 "memchr",
1136 "mio", 1331 "mio",
1137 "num_cpus", 1332 "num_cpus",
1138 "parking_lot", 1333 "parking_lot",
@@ -1140,18 +1335,18 @@ dependencies = [
1140 "signal-hook-registry", 1335 "signal-hook-registry",
1141 "socket2", 1336 "socket2",
1142 "tokio-macros", 1337 "tokio-macros",
1143 "windows-sys 0.42.0", 1338 "windows-sys 0.45.0",
1144] 1339]
1145 1340
1146[[package]] 1341[[package]]
1147name = "tokio-macros" 1342name = "tokio-macros"
1148version = "1.8.2" 1343version = "2.0.0"
1149source = "registry+https://github.com/rust-lang/crates.io-index" 1344source = "registry+https://github.com/rust-lang/crates.io-index"
1150checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" 1345checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
1151dependencies = [ 1346dependencies = [
1152 "proc-macro2", 1347 "proc-macro2",
1153 "quote", 1348 "quote",
1154 "syn", 1349 "syn 2.0.13",
1155] 1350]
1156 1351
1157[[package]] 1352[[package]]
@@ -1180,9 +1375,9 @@ dependencies = [
1180 1375
1181[[package]] 1376[[package]]
1182name = "toml" 1377name = "toml"
1183version = "0.7.2" 1378version = "0.7.3"
1184source = "registry+https://github.com/rust-lang/crates.io-index" 1379source = "registry+https://github.com/rust-lang/crates.io-index"
1185checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" 1380checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
1186dependencies = [ 1381dependencies = [
1187 "serde", 1382 "serde",
1188 "serde_spanned", 1383 "serde_spanned",
@@ -1201,15 +1396,15 @@ dependencies = [
1201 1396
1202[[package]] 1397[[package]]
1203name = "toml_edit" 1398name = "toml_edit"
1204version = "0.19.3" 1399version = "0.19.8"
1205source = "registry+https://github.com/rust-lang/crates.io-index" 1400source = "registry+https://github.com/rust-lang/crates.io-index"
1206checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5" 1401checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
1207dependencies = [ 1402dependencies = [
1208 "indexmap", 1403 "indexmap",
1209 "nom8",
1210 "serde", 1404 "serde",
1211 "serde_spanned", 1405 "serde_spanned",
1212 "toml_datetime", 1406 "toml_datetime",
1407 "winnow",
1213] 1408]
1214 1409
1215[[package]] 1410[[package]]
@@ -1246,15 +1441,15 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
1246 1441
1247[[package]] 1442[[package]]
1248name = "unicode-bidi" 1443name = "unicode-bidi"
1249version = "0.3.10" 1444version = "0.3.13"
1250source = "registry+https://github.com/rust-lang/crates.io-index" 1445source = "registry+https://github.com/rust-lang/crates.io-index"
1251checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" 1446checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
1252 1447
1253[[package]] 1448[[package]]
1254name = "unicode-ident" 1449name = "unicode-ident"
1255version = "1.0.6" 1450version = "1.0.8"
1256source = "registry+https://github.com/rust-lang/crates.io-index" 1451source = "registry+https://github.com/rust-lang/crates.io-index"
1257checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 1452checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
1258 1453
1259[[package]] 1454[[package]]
1260name = "unicode-normalization" 1455name = "unicode-normalization"
@@ -1283,6 +1478,12 @@ dependencies = [
1283] 1478]
1284 1479
1285[[package]] 1480[[package]]
1481name = "utf8parse"
1482version = "0.2.1"
1483source = "registry+https://github.com/rust-lang/crates.io-index"
1484checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
1485
1486[[package]]
1286name = "vcpkg" 1487name = "vcpkg"
1287version = "0.2.15" 1488version = "0.2.15"
1288source = "registry+https://github.com/rust-lang/crates.io-index" 1489source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1337,7 +1538,7 @@ dependencies = [
1337 "once_cell", 1538 "once_cell",
1338 "proc-macro2", 1539 "proc-macro2",
1339 "quote", 1540 "quote",
1340 "syn", 1541 "syn 1.0.109",
1341 "wasm-bindgen-shared", 1542 "wasm-bindgen-shared",
1342] 1543]
1343 1544
@@ -1371,7 +1572,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
1371dependencies = [ 1572dependencies = [
1372 "proc-macro2", 1573 "proc-macro2",
1373 "quote", 1574 "quote",
1374 "syn", 1575 "syn 1.0.109",
1375 "wasm-bindgen-backend", 1576 "wasm-bindgen-backend",
1376 "wasm-bindgen-shared", 1577 "wasm-bindgen-shared",
1377] 1578]
@@ -1437,18 +1638,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1437checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1638checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1438 1639
1439[[package]] 1640[[package]]
1641name = "windows"
1642version = "0.48.0"
1643source = "registry+https://github.com/rust-lang/crates.io-index"
1644checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
1645dependencies = [
1646 "windows-targets 0.48.0",
1647]
1648
1649[[package]]
1440name = "windows-sys" 1650name = "windows-sys"
1441version = "0.42.0" 1651version = "0.42.0"
1442source = "registry+https://github.com/rust-lang/crates.io-index" 1652source = "registry+https://github.com/rust-lang/crates.io-index"
1443checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1653checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
1444dependencies = [ 1654dependencies = [
1445 "windows_aarch64_gnullvm", 1655 "windows_aarch64_gnullvm 0.42.2",
1446 "windows_aarch64_msvc", 1656 "windows_aarch64_msvc 0.42.2",
1447 "windows_i686_gnu", 1657 "windows_i686_gnu 0.42.2",
1448 "windows_i686_msvc", 1658 "windows_i686_msvc 0.42.2",
1449 "windows_x86_64_gnu", 1659 "windows_x86_64_gnu 0.42.2",
1450 "windows_x86_64_gnullvm", 1660 "windows_x86_64_gnullvm 0.42.2",
1451 "windows_x86_64_msvc", 1661 "windows_x86_64_msvc 0.42.2",
1452] 1662]
1453 1663
1454[[package]] 1664[[package]]
@@ -1457,65 +1667,140 @@ version = "0.45.0"
1457source = "registry+https://github.com/rust-lang/crates.io-index" 1667source = "registry+https://github.com/rust-lang/crates.io-index"
1458checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1668checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
1459dependencies = [ 1669dependencies = [
1460 "windows-targets", 1670 "windows-targets 0.42.2",
1671]
1672
1673[[package]]
1674name = "windows-sys"
1675version = "0.48.0"
1676source = "registry+https://github.com/rust-lang/crates.io-index"
1677checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
1678dependencies = [
1679 "windows-targets 0.48.0",
1680]
1681
1682[[package]]
1683name = "windows-targets"
1684version = "0.42.2"
1685source = "registry+https://github.com/rust-lang/crates.io-index"
1686checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
1687dependencies = [
1688 "windows_aarch64_gnullvm 0.42.2",
1689 "windows_aarch64_msvc 0.42.2",
1690 "windows_i686_gnu 0.42.2",
1691 "windows_i686_msvc 0.42.2",
1692 "windows_x86_64_gnu 0.42.2",
1693 "windows_x86_64_gnullvm 0.42.2",
1694 "windows_x86_64_msvc 0.42.2",
1461] 1695]
1462 1696
1463[[package]] 1697[[package]]
1464name = "windows-targets" 1698name = "windows-targets"
1465version = "0.42.1" 1699version = "0.48.0"
1466source = "registry+https://github.com/rust-lang/crates.io-index" 1700source = "registry+https://github.com/rust-lang/crates.io-index"
1467checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 1701checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
1468dependencies = [ 1702dependencies = [
1469 "windows_aarch64_gnullvm", 1703 "windows_aarch64_gnullvm 0.48.0",
1470 "windows_aarch64_msvc", 1704 "windows_aarch64_msvc 0.48.0",
1471 "windows_i686_gnu", 1705 "windows_i686_gnu 0.48.0",
1472 "windows_i686_msvc", 1706 "windows_i686_msvc 0.48.0",
1473 "windows_x86_64_gnu", 1707 "windows_x86_64_gnu 0.48.0",
1474 "windows_x86_64_gnullvm", 1708 "windows_x86_64_gnullvm 0.48.0",
1475 "windows_x86_64_msvc", 1709 "windows_x86_64_msvc 0.48.0",
1476] 1710]
1477 1711
1478[[package]] 1712[[package]]
1479name = "windows_aarch64_gnullvm" 1713name = "windows_aarch64_gnullvm"
1480version = "0.42.1" 1714version = "0.42.2"
1715source = "registry+https://github.com/rust-lang/crates.io-index"
1716checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
1717
1718[[package]]
1719name = "windows_aarch64_gnullvm"
1720version = "0.48.0"
1721source = "registry+https://github.com/rust-lang/crates.io-index"
1722checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
1723
1724[[package]]
1725name = "windows_aarch64_msvc"
1726version = "0.42.2"
1481source = "registry+https://github.com/rust-lang/crates.io-index" 1727source = "registry+https://github.com/rust-lang/crates.io-index"
1482checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 1728checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
1483 1729
1484[[package]] 1730[[package]]
1485name = "windows_aarch64_msvc" 1731name = "windows_aarch64_msvc"
1486version = "0.42.1" 1732version = "0.48.0"
1487source = "registry+https://github.com/rust-lang/crates.io-index" 1733source = "registry+https://github.com/rust-lang/crates.io-index"
1488checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 1734checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
1489 1735
1490[[package]] 1736[[package]]
1491name = "windows_i686_gnu" 1737name = "windows_i686_gnu"
1492version = "0.42.1" 1738version = "0.42.2"
1739source = "registry+https://github.com/rust-lang/crates.io-index"
1740checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
1741
1742[[package]]
1743name = "windows_i686_gnu"
1744version = "0.48.0"
1745source = "registry+https://github.com/rust-lang/crates.io-index"
1746checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
1747
1748[[package]]
1749name = "windows_i686_msvc"
1750version = "0.42.2"
1493source = "registry+https://github.com/rust-lang/crates.io-index" 1751source = "registry+https://github.com/rust-lang/crates.io-index"
1494checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 1752checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
1495 1753
1496[[package]] 1754[[package]]
1497name = "windows_i686_msvc" 1755name = "windows_i686_msvc"
1498version = "0.42.1" 1756version = "0.48.0"
1757source = "registry+https://github.com/rust-lang/crates.io-index"
1758checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
1759
1760[[package]]
1761name = "windows_x86_64_gnu"
1762version = "0.42.2"
1499source = "registry+https://github.com/rust-lang/crates.io-index" 1763source = "registry+https://github.com/rust-lang/crates.io-index"
1500checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1764checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
1501 1765
1502[[package]] 1766[[package]]
1503name = "windows_x86_64_gnu" 1767name = "windows_x86_64_gnu"
1504version = "0.42.1" 1768version = "0.48.0"
1505source = "registry+https://github.com/rust-lang/crates.io-index" 1769source = "registry+https://github.com/rust-lang/crates.io-index"
1506checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1770checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
1507 1771
1508[[package]] 1772[[package]]
1509name = "windows_x86_64_gnullvm" 1773name = "windows_x86_64_gnullvm"
1510version = "0.42.1" 1774version = "0.42.2"
1511source = "registry+https://github.com/rust-lang/crates.io-index" 1775source = "registry+https://github.com/rust-lang/crates.io-index"
1512checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1776checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
1777
1778[[package]]
1779name = "windows_x86_64_gnullvm"
1780version = "0.48.0"
1781source = "registry+https://github.com/rust-lang/crates.io-index"
1782checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
1783
1784[[package]]
1785name = "windows_x86_64_msvc"
1786version = "0.42.2"
1787source = "registry+https://github.com/rust-lang/crates.io-index"
1788checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
1513 1789
1514[[package]] 1790[[package]]
1515name = "windows_x86_64_msvc" 1791name = "windows_x86_64_msvc"
1516version = "0.42.1" 1792version = "0.48.0"
1793source = "registry+https://github.com/rust-lang/crates.io-index"
1794checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
1795
1796[[package]]
1797name = "winnow"
1798version = "0.4.1"
1517source = "registry+https://github.com/rust-lang/crates.io-index" 1799source = "registry+https://github.com/rust-lang/crates.io-index"
1518checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1800checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
1801dependencies = [
1802 "memchr",
1803]
1519 1804
1520[[package]] 1805[[package]]
1521name = "winreg" 1806name = "winreg"
diff --git a/Cargo.toml b/Cargo.toml
index 7d55c00..51652f3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "modlist" 2name = "modlist"
3version = "0.11.1" 3version = "0.12.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
@@ -10,9 +10,11 @@ reqwest = { version = "0.11", features = ["json", "stream"] }
10tokio = { version = "1", features = ["full"] } 10tokio = { version = "1", features = ["full"] }
11serde = { version = "1.0", features = ["derive"] } 11serde = { version = "1.0", features = ["derive"] }
12serde_json = "1.0.87" 12serde_json = "1.0.87"
13rusqlite = { version = "0.28.0", features = ["bundled"] } 13rusqlite = { version = "0.29.0", features = ["bundled"] }
14futures-util = "0.3.14" 14futures-util = "0.3.14"
15chrono = "0.4.22" 15chrono = "0.4.22"
16toml = "0.7.2" 16toml = "0.7.2"
17error-chain = "0.12.4" 17error-chain = "0.12.4"
18dirs = "4.0.0" 18dirs = "5.0.0"
19clap = { version = "4.2.1", features = ["derive"] }
20clap_complete = "4.2.0"
diff --git a/src/apis/modrinth.rs b/src/apis/modrinth.rs
index bb5ee19..9afe7f3 100644
--- a/src/apis/modrinth.rs
+++ b/src/apis/modrinth.rs
@@ -2,7 +2,10 @@ use chrono::{DateTime, FixedOffset};
2use reqwest::Client; 2use reqwest::Client;
3use serde::Deserialize; 3use serde::Deserialize;
4 4
5use crate::{Modloader, List, error::{MLE, MLError, ErrorType}}; 5use crate::{
6 error::{ErrorType, MLError, MLE},
7 List, Modloader,
8};
6 9
7#[derive(Debug, Deserialize, Clone)] 10#[derive(Debug, Deserialize, Clone)]
8pub struct Project { 11pub struct Project {
@@ -47,7 +50,7 @@ pub struct ModeratorMessage {
47pub enum Side { 50pub enum Side {
48 required, 51 required,
49 optional, 52 optional,
50 unsupported 53 unsupported,
51} 54}
52 55
53#[allow(non_camel_case_types)] 56#[allow(non_camel_case_types)]
@@ -55,7 +58,7 @@ pub enum Side {
55pub enum Type { 58pub enum Type {
56 r#mod, 59 r#mod,
57 modpack, 60 modpack,
58 recourcepack 61 recourcepack,
59} 62}
60 63
61#[allow(non_camel_case_types)] 64#[allow(non_camel_case_types)]
@@ -63,9 +66,11 @@ pub enum Type {
63pub enum Status { 66pub enum Status {
64 approved, 67 approved,
65 rejected, 68 rejected,
66 draft, unlisted, archived, 69 draft,
70 unlisted,
71 archived,
67 processing, 72 processing,
68 unknown 73 unknown,
69} 74}
70 75
71#[derive(Debug, Clone, Deserialize)] 76#[derive(Debug, Clone, Deserialize)]
@@ -90,7 +95,7 @@ pub struct Version {
90pub enum VersionType { 95pub enum VersionType {
91 release, 96 release,
92 beta, 97 beta,
93 alpha 98 alpha,
94} 99}
95 100
96#[derive(Debug, Clone, Deserialize)] 101#[derive(Debug, Clone, Deserialize)]
@@ -110,22 +115,19 @@ pub struct Hash {
110 115
111async fn get(api: &str, path: String) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> { 116async fn get(api: &str, path: String) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
112 let url = format!(r#"{}{}"#, api, path); 117 let url = format!(r#"{}{}"#, api, path);
113 118
114 let client = Client::builder() 119 let client = Client::builder()
115 .user_agent(format!("fxqnlr/modlistcli/{} ([email protected])", env!("CARGO_PKG_VERSION"))) 120 .user_agent(format!(
121 "fxqnlr/modlistcli/{} ([email protected])",
122 env!("CARGO_PKG_VERSION")
123 ))
116 .build()?; 124 .build()?;
117 let res = client.get(url) 125 let res = client.get(url).send().await?;
118 .send() 126
119 .await?;
120
121 let mut data: Option<Vec<u8>> = None; 127 let mut data: Option<Vec<u8>> = None;
122 128
123 if res.status() == 200 { 129 if res.status() == 200 {
124 data = Some(res 130 data = Some(res.bytes().await?.to_vec());
125 .bytes()
126 .await?
127 .to_vec()
128 );
129 } 131 }
130 132
131 Ok(data) 133 Ok(data)
@@ -143,7 +145,7 @@ pub async fn projects(api: &str, ids: Vec<String>) -> Vec<Project> {
143 let url = format!(r#"projects?ids=["{}"]"#, all); 145 let url = format!(r#"projects?ids=["{}"]"#, all);
144 146
145 let data = get(api, url).await.unwrap().unwrap(); 147 let data = get(api, url).await.unwrap().unwrap();
146 148
147 serde_json::from_slice(&data).unwrap() 149 serde_json::from_slice(&data).unwrap()
148} 150}
149 151
@@ -154,7 +156,10 @@ pub async fn versions(api: &str, id: String, list: List) -> Vec<Version> {
154 Modloader::Fabric => String::from("fabric"), 156 Modloader::Fabric => String::from("fabric"),
155 }; 157 };
156 158
157 let url = format!(r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#, id, loaderstr, list.mc_version); 159 let url = format!(
160 r#"project/{}/version?loaders=["{}"]&game_versions=["{}"]"#,
161 id, loaderstr, list.mc_version
162 );
158 163
159 let data = get(api, url).await.unwrap(); 164 let data = get(api, url).await.unwrap();
160 165
@@ -185,7 +190,7 @@ pub fn extract_current_version(versions: Vec<Version>) -> MLE<String> {
185 times.sort_by_key(|t| t.1); 190 times.sort_by_key(|t| t.1);
186 times.reverse(); 191 times.reverse();
187 Ok(times[0].0.to_string()) 192 Ok(times[0].0.to_string())
188 }, 193 }
189 _ => panic!("available_versions should never be negative"), 194 _ => panic!("available_versions should never be negative"),
190 } 195 }
191} 196}
@@ -205,16 +210,19 @@ pub struct MCVersion {
205} 210}
206 211
207pub async fn get_minecraft_version(api: &str, version: MCVersionType) -> String { 212pub async fn get_minecraft_version(api: &str, version: MCVersionType) -> String {
208 let data = get(api, String::from("tag/game_version")).await.unwrap().unwrap(); 213 let data = get(api, String::from("tag/game_version"))
214 .await
215 .unwrap()
216 .unwrap();
209 let mc_versions: Vec<MCVersion> = serde_json::from_slice(&data).unwrap(); 217 let mc_versions: Vec<MCVersion> = serde_json::from_slice(&data).unwrap();
210 let ver = match version { 218 let ver = match version {
211 MCVersionType::Release => { 219 MCVersionType::Release => {
212 let mut i = 0; 220 let mut i = 0;
213 while !mc_versions[i].major { 221 while !mc_versions[i].major {
214 i += 1; 222 i += 1;
215 }; 223 }
216 &mc_versions[i] 224 &mc_versions[i]
217 }, 225 }
218 MCVersionType::Latest => &mc_versions[0], 226 MCVersionType::Latest => &mc_versions[0],
219 MCVersionType::Specific => { 227 MCVersionType::Specific => {
220 println!("Not inplemented"); 228 println!("Not inplemented");
diff --git a/src/commands/download.rs b/src/commands/download.rs
index 2714630..9434591 100644
--- a/src/commands/download.rs
+++ b/src/commands/download.rs
@@ -1,10 +1,16 @@
1use crate::{files::{get_downloaded_versions, download_versions, delete_version, disable_version, clean_list_dir}, db::{userlist_get_all_current_versions_with_mods, lists_get_all_ids, lists_get}, modrinth::get_raw_versions, error::{MLE, ErrorType, MLError}}; 1use crate::{config::Cfg, get_current_list, List};
2use crate::{List, get_current_list, config::Cfg, input::Input}; 2use crate::{
3 3 db::{lists_get, lists_get_all_ids, userlist_get_all_current_versions_with_mods},
4pub async fn download(config: Cfg, input: Input) -> MLE<()> { 4 error::{ErrorType, MLError, MLE},
5 files::{
6 clean_list_dir, delete_version, disable_version, download_versions, get_downloaded_versions,
7 },
8 modrinth::get_raw_versions,
9};
5 10
11pub async fn download(config: Cfg, all_lists: bool, clean: bool, delete_old: bool) -> MLE<()> {
6 let mut liststack: Vec<List> = vec![]; 12 let mut liststack: Vec<List> = vec![];
7 if input.all_lists { 13 if all_lists {
8 let list_ids = lists_get_all_ids(config.clone())?; 14 let list_ids = lists_get_all_ids(config.clone())?;
9 for id in list_ids { 15 for id in list_ids {
10 liststack.push(lists_get(config.clone(), id)?); 16 liststack.push(lists_get(config.clone(), id)?);
@@ -18,7 +24,10 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> {
18 for current_list in liststack { 24 for current_list in liststack {
19 let downloaded_versions = get_downloaded_versions(current_list.clone())?; 25 let downloaded_versions = get_downloaded_versions(current_list.clone())?;
20 println!("To download: {:#?}", downloaded_versions); 26 println!("To download: {:#?}", downloaded_versions);
21 let current_version_ids = match userlist_get_all_current_versions_with_mods(config.clone(), String::from(&current_list.id)) { 27 let current_version_ids = match userlist_get_all_current_versions_with_mods(
28 config.clone(),
29 String::from(&current_list.id),
30 ) {
22 Ok(i) => Ok(i), 31 Ok(i) => Ok(i),
23 Err(e) => Err(MLError::new(ErrorType::DBError, e.to_string().as_str())), 32 Err(e) => Err(MLError::new(ErrorType::DBError, e.to_string().as_str())),
24 }?; 33 }?;
@@ -33,31 +42,40 @@ pub async fn download(config: Cfg, input: Input) -> MLE<()> {
33 42
34 let current_download = downloaded_versions.get(&mod_id); 43 let current_download = downloaded_versions.get(&mod_id);
35 44
36 if current_download.is_none() || input.clean { 45 if current_download.is_none() || clean {
37 to_download.push(current_version); 46 to_download.push(current_version);
38 } else { 47 } else {
39 let downloaded_version = current_download.ok_or("SOMETHING_HAS_REALLY_GONE_WRONG").unwrap(); 48 let downloaded_version = current_download
49 .ok_or("SOMETHING_HAS_REALLY_GONE_WRONG")
50 .unwrap();
40 if &current_version != downloaded_version { 51 if &current_version != downloaded_version {
41 to_disable.push((mod_id.clone(), String::from(downloaded_version))); 52 to_disable.push((mod_id.clone(), String::from(downloaded_version)));
42 to_download.push(current_version); 53 to_download.push(current_version);
43 } 54 }
44 } 55 }
45 } 56 }
46 57
47 if input.clean { clean_list_dir(&current_list)? }; 58 if clean {
59 clean_list_dir(&current_list)?
60 };
48 61
49 if !to_download.is_empty() { 62 if !to_download.is_empty() {
50 download_versions(current_list.clone(), config.clone(), get_raw_versions(&config.apis.modrinth, to_download).await).await?; 63 download_versions(
64 current_list.clone(),
65 config.clone(),
66 get_raw_versions(&config.apis.modrinth, to_download).await,
67 )
68 .await?;
51 } else { 69 } else {
52 println!("There are no new versions to download"); 70 println!("There are no new versions to download");
53 } 71 }
54 72
55 if !to_disable.is_empty() { 73 if !to_disable.is_empty() {
56 for ver in to_disable { 74 for ver in to_disable {
57 if input.delete_old { 75 if delete_old {
58 println!("Deleting version {} for mod {}", ver.1, ver.0); 76 println!("Deleting version {} for mod {}", ver.1, ver.0);
59 delete_version(current_list.clone(), ver.1)?; 77 delete_version(current_list.clone(), ver.1)?;
60 } else { 78 } else {
61 disable_version(config.clone(), current_list.clone(), ver.1, ver.0)?; 79 disable_version(config.clone(), current_list.clone(), ver.1, ver.0)?;
62 }; 80 };
63 } 81 }
diff --git a/src/commands/io.rs b/src/commands/io.rs
index a3d056f..7f03eec 100644
--- a/src/commands/io.rs
+++ b/src/commands/io.rs
@@ -1,12 +1,18 @@
1use serde::{Deserialize, Serialize};
1use std::fs::File; 2use std::fs::File;
2use std::io::prelude::*; 3use std::io::prelude::*;
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, List, devdir, error::MLE, mods_add, IDSelector}; 5use crate::{
6 config::Cfg,
7 db::{lists_get, lists_get_all_ids, lists_insert, userlist_get_all_ids},
8 devdir,
9 error::MLE,
10 mod_add, IDSelector, List, Modloader,
11};
6 12
7#[derive(Debug, Serialize, Deserialize)] 13#[derive(Debug, Serialize, Deserialize)]
8struct Export { 14struct Export {
9 lists: Vec<ExportList> 15 lists: Vec<ExportList>,
10} 16}
11 17
12#[derive(Debug, Serialize, Deserialize)] 18#[derive(Debug, Serialize, Deserialize)]
@@ -20,73 +26,77 @@ struct ExportList {
20 26
21impl ExportList { 27impl ExportList {
22 pub fn from(config: Cfg, list_id: String, download: bool) -> MLE<Self> { 28 pub fn from(config: Cfg, list_id: String, download: bool) -> MLE<Self> {
23
24 let list = lists_get(config.clone(), String::from(&list_id))?; 29 let list = lists_get(config.clone(), String::from(&list_id))?;
25 30
26 let mut dl_folder = None; 31 let mut dl_folder = None;
27 if download{ dl_folder = Some(list.download_folder) }; 32 if download {
33 dl_folder = Some(list.download_folder)
34 };
28 35
29 let mods = userlist_get_all_ids(config, list_id)?.join("|"); 36 let mods = userlist_get_all_ids(config, list_id)?.join("|");
30 37
31 Ok(Self { id: list.id, mods, launcher: list.modloader.to_string(), mc_version: list.mc_version, download_folder: dl_folder }) 38 Ok(Self {
39 id: list.id,
40 mods,
41 launcher: list.modloader.to_string(),
42 mc_version: list.mc_version,
43 download_folder: dl_folder,
44 })
32 } 45 }
33} 46}
34 47
35pub async fn io(config: Cfg, input: Input) -> MLE<()> { 48pub fn export(config: Cfg, list: Option<String>) -> MLE<()> {
36
37 match input.clone().io_options.unwrap() {
38 IoOptions::Export => { export(config, input)? },
39 IoOptions::Import => { import(config, input).await? },
40 }
41
42 Ok(())
43}
44
45fn export(config: Cfg, input: Input) -> MLE<()> {
46 let mut list_ids: Vec<String> = vec![]; 49 let mut list_ids: Vec<String> = vec![];
47 if input.all_lists { 50 if list.is_none() {
48 list_ids = lists_get_all_ids(config.clone())?; 51 list_ids = lists_get_all_ids(config.clone())?;
49 } else { 52 } else {
50 list_ids.push(lists_get(config.clone(), input.list.unwrap().id)?.id); 53 list_ids.push(lists_get(config.clone(), list.unwrap())?.id);
51 } 54 }
52 let mut lists: Vec<ExportList> = vec![]; 55 let mut lists: Vec<ExportList> = vec![];
53 for list_id in list_ids { 56 for list_id in list_ids {
54 lists.push(ExportList::from(config.clone(), list_id, true)?); 57 lists.push(ExportList::from(config.clone(), list_id, true)?);
55 } 58 }
56 59
57 let toml = toml::to_string( &Export { lists } )?; 60 let toml = toml::to_string(&Export { lists })?;
58 61
59 let filestr = dirs::home_dir().unwrap().join("mlexport.toml"); 62 let filestr = dirs::home_dir().unwrap().join("mlexport.toml");
60 63
61 let mut file = File::create(devdir(filestr.into_os_string().into_string().unwrap().as_str()))?; 64 let mut file = File::create(devdir(
65 filestr.into_os_string().into_string().unwrap().as_str(),
66 ))?;
62 file.write_all(toml.as_bytes())?; 67 file.write_all(toml.as_bytes())?;
63 68
64 Ok(()) 69 Ok(())
65} 70}
66 71
67async fn import(config: Cfg, input: Input) -> MLE<()> { 72pub async fn import(config: Cfg, file_str: String, direct_download: bool) -> MLE<()> {
68 73 let mut file = File::open(file_str)?;
69 let filestr: String = match input.file {
70 Some(args) => args,
71 None => devdir(dirs::home_dir().unwrap().join("mlexport.toml").into_os_string().into_string().unwrap().as_str()),
72 };
73
74 let mut file = File::open(filestr)?;
75 let mut content = String::new(); 74 let mut content = String::new();
76 file.read_to_string(&mut content)?; 75 file.read_to_string(&mut content)?;
77 let export: Export = toml::from_str(&content)?; 76 let export: Export = toml::from_str(&content)?;
78 77
79 for exportlist in export.lists { 78 for exportlist in export.lists {
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() }; 79 let list = List {
81 lists_insert(config.clone(), list.id.clone(), list.mc_version.clone(), list.modloader.clone(), String::from(&list.download_folder))?; 80 id: exportlist.id,
81 mc_version: exportlist.mc_version,
82 modloader: Modloader::from(&exportlist.launcher)?,
83 download_folder: exportlist.download_folder.ok_or("NO_DL").unwrap(),
84 };
85 lists_insert(
86 config.clone(),
87 list.id.clone(),
88 list.mc_version.clone(),
89 list.modloader.clone(),
90 String::from(&list.download_folder),
91 )?;
82 let mods: Vec<&str> = exportlist.mods.split('|').collect(); 92 let mods: Vec<&str> = exportlist.mods.split('|').collect();
83 let mut mod_ids = vec![]; 93 let mut mod_ids = vec![];
84 for mod_id in mods { 94 for mod_id in mods {
85 mod_ids.push(IDSelector::ModificationID(String::from(mod_id))); 95 mod_ids.push(IDSelector::ModificationID(String::from(mod_id)));
86 }; 96 }
87 //TODO impl set_version and good direct download 97 //TODO impl set_version and good direct download
88 //TODO impl all at once, dafuck 98 //TODO impl all at once, dafuck
89 mods_add(config.clone(), mod_ids, list, input.direct_download, false).await?; 99 mod_add(config.clone(), mod_ids, list, direct_download, false).await?;
90 } 100 }
91 Ok(()) 101 Ok(())
92} 102}
diff --git a/src/commands/list.rs b/src/commands/list.rs
index 8e86973..13176f4 100644
--- a/src/commands/list.rs
+++ b/src/commands/list.rs
@@ -1,4 +1,12 @@
1use crate::{db::{lists_insert, lists_remove, config_change_current_list, config_get_current_list, lists_get, lists_version}, Modloader, config::Cfg, input::{Input, ListOptions}, cmd_update, error::MLE}; 1use crate::{
2 config::Cfg,
3 db::{
4 config_change_current_list, config_get_current_list, lists_get, lists_insert, lists_remove,
5 lists_version,
6 },
7 error::MLE,
8 update, Modloader,
9};
2 10
3#[derive(Debug, Clone, PartialEq, Eq)] 11#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct List { 12pub struct List {
@@ -8,60 +16,55 @@ pub struct List {
8 pub download_folder: String, 16 pub download_folder: String,
9} 17}
10 18
11pub async fn list(config: Cfg, input: Input) -> MLE<()> {
12
13 match input.clone().list_options.unwrap() {
14 ListOptions::Add => {
15 add(config, input)
16 },
17 ListOptions::Change => {
18 change(config, input)
19 },
20 ListOptions::Remove => {
21 remove(config, input)
22 },
23 ListOptions::Version => {
24 version(config, input).await
25 }
26 }
27}
28
29pub fn get_current_list(config: Cfg) -> MLE<List> { 19pub fn get_current_list(config: Cfg) -> MLE<List> {
30 let id = config_get_current_list(config.clone())?; 20 let id = config_get_current_list(config.clone())?;
31 lists_get(config, id) 21 lists_get(config, id)
32} 22}
33 23
34fn add(config: Cfg, input: Input) -> MLE<()> { 24pub fn list_add(
35 let id = input.list_id.unwrap(); 25 config: Cfg,
36 let mc_version = input.list_mcversion.unwrap(); 26 id: String,
37 let mod_loader = input.modloader.unwrap(); 27 mc_version: String,
38 let download_folder = input.directory.unwrap(); 28 modloader: Modloader,
39 lists_insert(config, id, mc_version, mod_loader, download_folder) 29 directory: String,
30) -> MLE<()> {
31 lists_insert(config, id, mc_version, modloader, directory)
40} 32}
41 33
42fn change(config: Cfg, input: Input) -> MLE<()> { 34pub fn list_change(config: Cfg, id: String) -> MLE<()> {
43 println!("Change default list to: {}", input.clone().list.unwrap().id); 35 //TODO check if list exists
44 config_change_current_list(config, input.list.unwrap().id) 36 println!("Change default list to: {}", id);
37 config_change_current_list(config, id)
45} 38}
46 39
47fn remove(config: Cfg, input: Input) -> MLE<()> { 40pub fn list_remove(config: Cfg, id: String) -> MLE<()> {
48 lists_remove(config, input.list.unwrap().id) 41 lists_remove(config, id)
49} 42}
50 43
51///Changing the current lists version and updating it 44///Changing the current lists version and updating it
52/// 45///
53/// #Arguments 46/// #Arguments
54/// 47///
55/// * `config` - The current config 48/// * `config` - The current config
56/// * `args` - All args, to extract the new version 49/// * `args` - All args, to extract the new version
57async fn version(config: Cfg, input: Input) -> MLE<()> { 50pub async fn list_version(
58 println!("Change version for list {} to minecraft version: {}", input.clone().list.unwrap().id, input.clone().list_mcversion.unwrap()); 51 config: Cfg,
52 id: String,
53 mc_version: String,
54 download: bool,
55 delete: bool,
56) -> MLE<()> {
57 println!(
58 "Change version for list {} to minecraft version: {}",
59 id, mc_version
60 );
59 61
60 lists_version(config.clone(), input.clone().list.ok_or("").unwrap().id, input.clone().list_mcversion.ok_or("").unwrap())?; 62 lists_version(config.clone(), &id, &mc_version)?;
61
62 //Linebreak readability
63 println!("");
64 63
65 println!("Check for updates for new minecraft version in list {}", input.clone().list.unwrap().id); 64 println!(
66 cmd_update(config, vec![input.list.ok_or("").unwrap()], true, input.direct_download, input.delete_old).await 65 "\nCheck for updates for new minecraft version in list {}",
66 id
67 );
68 let list = lists_get(config.clone(), id)?;
69 update(config, vec![list], true, download, delete).await
67} 70}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 0d5bd00..1c7c012 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -1,13 +1,13 @@
1pub mod modification;
2pub mod list;
3pub mod update;
4pub mod setup;
5pub mod download; 1pub mod download;
6pub mod io; 2pub mod io;
3pub mod list;
4pub mod modification;
5pub mod setup;
6pub mod update;
7 7
8pub use modification::*;
9pub use list::*;
10pub use update::*;
11pub use setup::*;
12pub use download::*; 8pub use download::*;
13pub use io::*; 9pub use io::*;
10pub use list::*;
11pub use modification::*;
12pub use setup::*;
13pub use update::*;
diff --git a/src/commands/modification.rs b/src/commands/modification.rs
index 31e50af..ffc4e10 100644
--- a/src/commands/modification.rs
+++ b/src/commands/modification.rs
@@ -1,27 +1,19 @@
1use crate::{modrinth::{versions, extract_current_version, Version, projects, get_raw_versions, project}, 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}}; 1use crate::{
2 config::Cfg,
3 db::{
4 lists_get_all_ids, mods_get_id, mods_insert, mods_remove, userlist_get_all_ids,
5 userlist_get_current_version, userlist_insert, userlist_remove,
6 },
7 error::{ErrorType, MLError, MLE},
8 files::{delete_version, download_versions},
9 modrinth::{extract_current_version, get_raw_versions, project, projects, versions, Version},
10 List,
11};
2 12
3#[derive(Debug, Clone, PartialEq, Eq)] 13#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum IDSelector { 14pub enum IDSelector {
5 ModificationID(String), 15 ModificationID(String),
6 VersionID(String) 16 VersionID(String),
7}
8
9pub async fn modification(config: Cfg, input: Input) -> MLE<()> {
10 match input.clone().mod_options.ok_or("").unwrap() {
11 ModOptions::Add => {
12 add(config, input).await
13 },
14 ModOptions::Remove => {
15 remove(config, input)
16 },
17 }
18}
19
20async fn add(config: Cfg, input: Input) -> MLE<()> {
21
22 mods_add(config, vec![input.mod_id.unwrap()], input.list.unwrap(), input.direct_download, input.set_version).await?;
23
24 Ok(())
25} 17}
26 18
27#[derive(Debug, Clone)] 19#[derive(Debug, Clone)]
@@ -34,10 +26,16 @@ pub struct ProjectInfo {
34 pub download_link: String, 26 pub download_link: String,
35} 27}
36 28
37pub async fn mods_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_download: bool, set_version: bool) -> MLE<()> { 29pub async fn mod_add(
30 config: Cfg,
31 ids: Vec<IDSelector>,
32 list: List,
33 direct_download: bool,
34 set_version: bool,
35) -> MLE<()> {
38 println!("Add mods to {}", list.id); 36 println!("Add mods to {}", list.id);
39 println!(" └Add mods:"); 37 println!(" └Add mods:");
40 38
41 let mut mod_ids: Vec<String> = Vec::new(); 39 let mut mod_ids: Vec<String> = Vec::new();
42 let mut ver_ids: Vec<String> = Vec::new(); 40 let mut ver_ids: Vec<String> = Vec::new();
43 41
@@ -50,11 +48,17 @@ pub async fn mods_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_down
50 } 48 }
51 49
52 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 50 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
53 if !mod_ids.is_empty() { projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?) }; 51 if !mod_ids.is_empty() {
54 if !ver_ids.is_empty() { projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?) }; 52 projectinfo.append(&mut get_mod_infos(config.clone(), mod_ids, list.clone()).await?)
53 };
54 if !ver_ids.is_empty() {
55 projectinfo.append(&mut get_ver_info(config.clone(), ver_ids).await?)
56 };
57
58 if projectinfo.is_empty() {
59 return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?"));
60 };
55 61
56 if projectinfo.is_empty() { return Err(MLError::new(ErrorType::ArgumentError, "NO_IDS?")) };
57
58 let mut downloadstack: Vec<Version> = Vec::new(); 62 let mut downloadstack: Vec<Version> = Vec::new();
59 63
60 //Adding each mod to the lists and downloadstack 64 //Adding each mod to the lists and downloadstack
@@ -63,29 +67,59 @@ pub async fn mods_add(config: Cfg, ids: Vec<IDSelector>, list: List, direct_down
63 } else { 67 } else {
64 println!(" └Insert mods in list {} and save infos", list.id); 68 println!(" └Insert mods in list {} and save infos", list.id);
65 } 69 }
66 70
67 for project in projectinfo { 71 for project in projectinfo {
68 let current_version_id = if project.current_version.is_none() { String::from("NONE") } else { project.current_version.clone().unwrap().id }; 72 let current_version_id = if project.current_version.is_none() {
69 match userlist_insert(config.clone(), &list.id, &project.mod_id, &current_version_id, project.clone().applicable_versions, &project.download_link, set_version) { 73 String::from("NONE")
74 } else {
75 project.current_version.clone().unwrap().id
76 };
77 match userlist_insert(
78 config.clone(),
79 &list.id,
80 &project.mod_id,
81 &current_version_id,
82 project.clone().applicable_versions,
83 &project.download_link,
84 set_version,
85 ) {
70 Err(e) => { 86 Err(e) => {
71 let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id); 87 let expected_err = format!("SQL: UNIQUE constraint failed: {}.mod_id", list.id);
72 if e.to_string() == expected_err { Err(MLError::new(ErrorType::ModError, "MOD_ALREADY_ON_SELECTED_LIST")) } else { Err(e) } 88 if e.to_string() == expected_err {
73 }, 89 Err(MLError::new(
74 Ok(..) => { Ok(..) }, 90 ErrorType::ModError,
91 "MOD_ALREADY_ON_SELECTED_LIST",
92 ))
93 } else {
94 Err(e)
95 }
96 }
97 Ok(..) => Ok(..),
75 }?; 98 }?;
76 99
77 match mods_insert(config.clone(), &project.mod_id, &project.slug, &project.title) { 100 match mods_insert(
101 config.clone(),
102 &project.mod_id,
103 &project.slug,
104 &project.title,
105 ) {
78 Err(e) => { 106 Err(e) => {
79 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" { Ok(..) } else { Err(e) } 107 if e.to_string() == "SQL: UNIQUE constraint failed: mods.id" {
80 }, 108 Ok(..)
109 } else {
110 Err(e)
111 }
112 }
81 Ok(..) => Ok(..), 113 Ok(..) => Ok(..),
82 }?; 114 }?;
83 115
84 if project.current_version.is_some() { downloadstack.push(project.current_version.unwrap()) }; 116 if project.current_version.is_some() {
117 downloadstack.push(project.current_version.unwrap())
118 };
85 } 119 }
86 120
87 //Download all the added mods 121 //Download all the added mods
88 if direct_download { 122 if direct_download {
89 download_versions(list.clone(), config.clone(), downloadstack).await?; 123 download_versions(list.clone(), config.clone(), downloadstack).await?;
90 }; 124 };
91 125
@@ -104,7 +138,12 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<String>, list: List) -> MLE<Vec
104 for project in m_projects { 138 for project in m_projects {
105 println!("\t└{}", project.title); 139 println!("\t└{}", project.title);
106 println!("\t └Get versions"); 140 println!("\t └Get versions");
107 let available_versions = versions(&config.apis.modrinth, String::from(&project.id), list.clone()).await; 141 let available_versions = versions(
142 &config.apis.modrinth,
143 String::from(&project.id),
144 list.clone(),
145 )
146 .await;
108 147
109 let mut available_versions_vec: Vec<String> = Vec::new(); 148 let mut available_versions_vec: Vec<String> = Vec::new();
110 let current_version: Option<Version>; 149 let current_version: Option<Version>;
@@ -113,36 +152,63 @@ async fn get_mod_infos(config: Cfg, mod_ids: Vec<String>, list: List) -> MLE<Vec
113 let current_id = extract_current_version(available_versions.clone())?; 152 let current_id = extract_current_version(available_versions.clone())?;
114 println!("\t └Current version: {}", current_id); 153 println!("\t └Current version: {}", current_id);
115 154
116 current_version = Some(available_versions.clone().into_iter().find(|v| v.id == current_id).unwrap()); 155 current_version = Some(
156 available_versions
157 .clone()
158 .into_iter()
159 .find(|v| v.id == current_id)
160 .unwrap(),
161 );
117 162
118 file = current_version.clone().ok_or("").unwrap().files.into_iter().find(|f| f.primary).unwrap().url; 163 file = current_version
164 .clone()
165 .ok_or("")
166 .unwrap()
167 .files
168 .into_iter()
169 .find(|f| f.primary)
170 .unwrap()
171 .url;
119 for ver in available_versions { 172 for ver in available_versions {
120 available_versions_vec.push(ver.id); 173 available_versions_vec.push(ver.id);
121 }; 174 }
122
123 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version, applicable_versions: available_versions_vec, download_link: file })
124 175
176 projectinfo.push(ProjectInfo {
177 mod_id: project.id,
178 slug: project.slug,
179 title: project.title,
180 current_version,
181 applicable_versions: available_versions_vec,
182 download_link: file,
183 })
125 } else { 184 } else {
126 println!("\t └There's currently no mod version for your specified target"); 185 println!("\t └There's currently no mod version for your specified target");
127 current_version = None; 186 current_version = None;
128 file = String::from("NONE"); 187 file = String::from("NONE");
129 available_versions_vec.push(String::from("NONE")); 188 available_versions_vec.push(String::from("NONE"));
130 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version, applicable_versions: available_versions_vec, download_link: file }) 189 projectinfo.push(ProjectInfo {
190 mod_id: project.id,
191 slug: project.slug,
192 title: project.title,
193 current_version,
194 applicable_versions: available_versions_vec,
195 download_link: file,
196 })
131 } 197 }
132 }; 198 }
133 199
134 Ok(projectinfo) 200 Ok(projectinfo)
135} 201}
136 202
137async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>> { 203async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>> {
138 let mut projectinfo: Vec<ProjectInfo> = Vec::new(); 204 let mut projectinfo: Vec<ProjectInfo> = Vec::new();
139 205
140 //Get required information from ver_ids 206 //Get required information from ver_ids
141 let mut v_versions = get_raw_versions(&config.apis.modrinth, ver_ids).await; 207 let mut v_versions = get_raw_versions(&config.apis.modrinth, ver_ids).await;
142 let mut v_mod_ids: Vec<String> = Vec::new(); 208 let mut v_mod_ids: Vec<String> = Vec::new();
143 for ver in v_versions.clone() { 209 for ver in v_versions.clone() {
144 v_mod_ids.push(ver.project_id); 210 v_mod_ids.push(ver.project_id);
145 }; 211 }
146 let mut v_projects = projects(&config.apis.modrinth, v_mod_ids).await; 212 let mut v_projects = projects(&config.apis.modrinth, v_mod_ids).await;
147 v_versions.sort_by(|a, b| a.project_id.cmp(&b.project_id)); 213 v_versions.sort_by(|a, b| a.project_id.cmp(&b.project_id));
148 v_projects.sort_by(|a, b| a.id.cmp(&b.id)); 214 v_projects.sort_by(|a, b| a.id.cmp(&b.id));
@@ -150,35 +216,54 @@ async fn get_ver_info(config: Cfg, ver_ids: Vec<String>) -> MLE<Vec<ProjectInfo>
150 for (i, project) in v_projects.into_iter().enumerate() { 216 for (i, project) in v_projects.into_iter().enumerate() {
151 let version = &v_versions[i]; 217 let version = &v_versions[i];
152 println!("\t└{}({})", project.title, version.id); 218 println!("\t└{}({})", project.title, version.id);
153 let file = version.clone().files.into_iter().find(|f| f.primary).unwrap().url; 219 let file = version
154 projectinfo.push(ProjectInfo { mod_id: project.id, slug: project.slug, title: project.title, current_version: Some(version.clone()), applicable_versions: vec![String::from(&version.id)], download_link: file }) 220 .clone()
155 }; 221 .files
222 .into_iter()
223 .find(|f| f.primary)
224 .unwrap()
225 .url;
226 projectinfo.push(ProjectInfo {
227 mod_id: project.id,
228 slug: project.slug,
229 title: project.title,
230 current_version: Some(version.clone()),
231 applicable_versions: vec![String::from(&version.id)],
232 download_link: file,
233 })
234 }
156 Ok(projectinfo) 235 Ok(projectinfo)
157} 236}
158 237
159fn remove(config: Cfg, input: Input) -> MLE<()> { 238/// Remove mod from a list
160 239/// # Arguments
161 let id = match input.clone().mod_id.unwrap() { 240///
162 IDSelector::ModificationID(id) => id, 241/// * `config` - config struct
163 IDSelector::VersionID(..) => return Err(MLError::new(ErrorType::ArgumentError, "NO_MOD_ID")), 242/// * `id` - name, slug or id of the mod
164 }; 243/// * `list` - List struct
165 244pub fn mod_remove(config: Cfg, id: &str, list: List) -> MLE<()> {
166 let mod_id = mods_get_id(&config.data, &id)?; 245 let mod_id = mods_get_id(&config.data, id)?;
167 246
168 let version = userlist_get_current_version(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; 247 let version = userlist_get_current_version(config.clone(), &list.id, &mod_id)?;
169 248
170 userlist_remove(config.clone(), input.clone().list.unwrap().id, String::from(&mod_id))?; 249 userlist_remove(config.clone(), &list.id, &mod_id)?;
171 delete_version(input.list.unwrap(), version)?; 250 delete_version(list, version)?;
172 251
173 let list_ids = lists_get_all_ids(config.clone())?; 252 let list_ids = lists_get_all_ids(config.clone())?;
174 253
254 // Remove mod from main list if not used elsewhere
175 let mut mod_used = false; 255 let mut mod_used = false;
176 for id in list_ids { 256 for id in list_ids {
177 let mods = userlist_get_all_ids(config.clone(), id)?; 257 let mods = userlist_get_all_ids(config.clone(), id)?;
178 if mods.contains(&mod_id) { mod_used = true; break; }; 258 if mods.contains(&mod_id) {
179 }; 259 mod_used = true;
260 break;
261 };
262 }
180 263
181 if !mod_used { mods_remove(config, mod_id)?; }; 264 if !mod_used {
265 mods_remove(config, mod_id)?;
266 };
182 267
183 Ok(()) 268 Ok(())
184} 269}
diff --git a/src/commands/setup.rs b/src/commands/setup.rs
index 0161bd7..40e8c0a 100644
--- a/src/commands/setup.rs
+++ b/src/commands/setup.rs
@@ -1,14 +1,14 @@
1use std::{fs::File, path::Path}; 1use std::{fs::File, path::Path};
2 2
3use crate::{config::Cfg, db::db_setup, error::MLE, devdir}; 3use crate::{config::Cfg, db::db_setup, devdir, error::MLE};
4 4
5pub async fn setup(config: Cfg) -> MLE<()> { 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 create(config, db_file)?; 9 create(config, db_file)?;
10 } 10 }
11 11
12 /* 12 /*
13 match s_config_get_version(config.clone()) { 13 match s_config_get_version(config.clone()) {
14 Ok(ver) => { 14 Ok(ver) => {
@@ -21,12 +21,11 @@ pub async fn setup(config: Cfg) -> MLE<()> {
21 Err(..) => to_02(config).await? 21 Err(..) => to_02(config).await?
22 }; 22 };
23 */ 23 */
24 24
25 Ok(()) 25 Ok(())
26} 26}
27 27
28fn create(config: Cfg, db_file: String) -> MLE<()> { 28fn create(config: Cfg, db_file: String) -> MLE<()> {
29
30 println!("Create database"); 29 println!("Create database");
31 30
32 File::create(db_file)?; 31 File::create(db_file)?;
@@ -44,7 +43,7 @@ fn create(config: Cfg, db_file: String) -> MLE<()> {
44// let full_list = lists_get(config.clone(), String::from(&list))?; 43// let full_list = lists_get(config.clone(), String::from(&list))?;
45// 44//
46// let versions = userlist_get_all_current_version_ids(config.clone(), full_list.clone().id)?; 45// let versions = userlist_get_all_current_version_ids(config.clone(), full_list.clone().id)?;
47// 46//
48// let raw_versions = get_raw_versions(String::from(&config.apis.modrinth), versions).await; 47// let raw_versions = get_raw_versions(String::from(&config.apis.modrinth), versions).await;
49// 48//
50// for ver in raw_versions { 49// for ver in raw_versions {
@@ -69,4 +68,3 @@ fn create(config: Cfg, db_file: String) -> MLE<()> {
69// } 68// }
70// s_config_update_version(config, String::from("0.4")) 69// s_config_update_version(config, String::from("0.4"))
71//} 70//}
72
diff --git a/src/commands/update.rs b/src/commands/update.rs
index 75bee39..3d9578b 100644
--- a/src/commands/update.rs
+++ b/src/commands/update.rs
@@ -1,29 +1,30 @@
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, userlist_get_set_version, mods_get_info}, List, input::Input, files::{delete_version, download_versions, disable_version, clean_list_dir}, error::{MLE, MLError, ErrorType}}; 1use crate::{
2 2 config::Cfg,
3pub async fn update(config: Cfg, input: Input) -> MLE<()> { 3 db::{
4 let mut liststack: Vec<List> = vec![]; 4 mods_get_info, userlist_change_versions, userlist_get_all_ids,
5 if input.all_lists { 5 userlist_get_applicable_versions, userlist_get_current_version, userlist_get_set_version,
6 let list_ids = lists_get_all_ids(config.clone())?; 6 },
7 for id in list_ids { 7 error::{ErrorType, MLError, MLE},
8 liststack.push(lists_get(config.clone(), id)?); 8 files::{clean_list_dir, delete_version, disable_version, download_versions},
9 } 9 modrinth::{extract_current_version, versions, Version},
10 } else { 10 List,
11 let current = get_current_list(config.clone())?; 11};
12 println!("Update list {}:", current.id); 12
13 liststack.push(current) 13pub async fn update(
14 } 14 config: Cfg,
15 cmd_update(config, liststack, input.clean, input.direct_download, input.delete_old).await 15 liststack: Vec<List>,
16} 16 clean: bool,
17 17 direct_download: bool,
18pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_download: bool, delete_old: bool) -> MLE<()> { 18 delete_old: bool,
19) -> MLE<()> {
19 for current_list in liststack { 20 for current_list in liststack {
20 let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?; 21 let mods = userlist_get_all_ids(config.clone(), current_list.clone().id)?;
21 22
22 let mut current_versions: Vec<(String, String)> = vec![]; 23 let mut current_versions: Vec<(String, String)> = vec![];
23 24
24 println!(" └Update mods:"); 25 println!(" └Update mods:");
25 let mut updatestack: Vec<Version> = vec![]; 26 let mut updatestack: Vec<Version> = vec![];
26 27
27 for id in mods { 28 for id in mods {
28 let info = mods_get_info(config.clone(), &id)?; 29 let info = mods_get_info(config.clone(), &id)?;
29 println!("\t└{}", info.title); 30 println!("\t└{}", info.title);
@@ -34,27 +35,39 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
34 } 35 }
35 36
36 //Getting current installed version for disable or delete 37 //Getting current installed version for disable or delete
37 let disable_version = userlist_get_current_version(config.clone(), String::from(&current_list.id), String::from(&id))?; 38 let disable_version =
39 userlist_get_current_version(config.clone(), &current_list.id, &id)?;
38 40
39 updatestack.push( 41 updatestack.push(
40 match specific_update(config.clone(), clean, current_list.clone(), String::from(&id)).await { 42 match specific_update(
43 config.clone(),
44 clean,
45 current_list.clone(),
46 String::from(&id),
47 )
48 .await
49 {
41 Ok(ver) => { 50 Ok(ver) => {
42 current_versions.push((disable_version, id)); 51 current_versions.push((disable_version, id));
43 ver 52 ver
44 }, 53 }
45 Err(e) => { 54 Err(e) => {
46 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" { 55 if e.to_string() == "Mod: NO_UPDATE_AVAILABLE" {
47 println!("\t └No new version found for the specified minecraft version"); 56 println!(
57 "\t └No new version found for the specified minecraft version"
58 );
48 } else { 59 } else {
49 return Err(e); 60 return Err(e);
50 }; 61 };
51 continue; 62 continue;
52 } 63 }
53 } 64 },
54 ) 65 )
55 }; 66 }
56 67
57 if clean { clean_list_dir(&current_list)?; }; 68 if clean {
69 clean_list_dir(&current_list)?;
70 };
58 71
59 if direct_download && !updatestack.is_empty() { 72 if direct_download && !updatestack.is_empty() {
60 download_versions(current_list.clone(), config.clone(), updatestack).await?; 73 download_versions(current_list.clone(), config.clone(), updatestack).await?;
@@ -65,7 +78,7 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
65 if delete_old { 78 if delete_old {
66 println!("\t └Delete version {}", ver.0); 79 println!("\t └Delete version {}", ver.0);
67 delete_version(current_list.clone(), ver.0)?; 80 delete_version(current_list.clone(), ver.0)?;
68 } else if ver.0 != "NONE" { 81 } else if ver.0 != "NONE" {
69 println!("\t └Disable version {}", ver.0); 82 println!("\t └Disable version {}", ver.0);
70 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?; 83 disable_version(config.clone(), current_list.clone(), ver.0, ver.1)?;
71 }; 84 };
@@ -78,10 +91,11 @@ pub async fn cmd_update(config: Cfg, liststack: Vec<List>, clean: bool, direct_d
78} 91}
79 92
80async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE<Version> { 93async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> MLE<Version> {
81 let applicable_versions = versions(&config.apis.modrinth, String::from(&id), list.clone()).await; 94 let applicable_versions =
82 95 versions(&config.apis.modrinth, String::from(&id), list.clone()).await;
96
83 let mut versions: Vec<String> = vec![]; 97 let mut versions: Vec<String> = vec![];
84 98
85 if !applicable_versions.is_empty() { 99 if !applicable_versions.is_empty() {
86 for ver in &applicable_versions { 100 for ver in &applicable_versions {
87 versions.push(String::from(&ver.id)); 101 versions.push(String::from(&ver.id));
@@ -92,8 +106,14 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
92 106
93 let mut current: Vec<Version> = vec![]; 107 let mut current: Vec<Version> = vec![];
94 //TODO Split clean and no match 108 //TODO Split clean and no match
95 if clean || (versions.join("|") != userlist_get_applicable_versions(config.clone(), String::from(&list.id), String::from(&id))?) { 109 if clean
96 110 || (versions.join("|")
111 != userlist_get_applicable_versions(
112 config.clone(),
113 String::from(&list.id),
114 String::from(&id),
115 )?)
116 {
97 let current_str = extract_current_version(applicable_versions.clone())?; 117 let current_str = extract_current_version(applicable_versions.clone())?;
98 118
99 if clean { 119 if clean {
@@ -104,34 +124,54 @@ async fn specific_update(config: Cfg, clean: bool, list: List, id: String) -> ML
104 }; 124 };
105 125
106 //get new versions 126 //get new versions
107 let current_ver = match applicable_versions.into_iter().find(|ver| ver.id == current_str).ok_or("!no current version in applicable_versions") { 127 let current_ver = match applicable_versions
128 .into_iter()
129 .find(|ver| ver.id == current_str)
130 .ok_or("!no current version in applicable_versions")
131 {
108 Ok(v) => Ok(v), 132 Ok(v) => Ok(v),
109 Err(e) => Err(MLError::new(ErrorType::Other, e)), 133 Err(e) => Err(MLError::new(ErrorType::Other, e)),
110 }?; 134 }?;
111 current.push(current_ver.clone()); 135 current.push(current_ver.clone());
112 136
113 let link = match current_ver.files.into_iter().find(|f| f.primary).ok_or("!no primary in links") { 137 //TODO implement version selection if no primary
138 let link = match current_ver
139 .files
140 .into_iter()
141 .find(|f| f.primary)
142 .ok_or("!no primary in links")
143 {
114 Ok(p) => Ok(p), 144 Ok(p) => Ok(p),
115 Err(e) => Err(MLError::new(ErrorType::Other, e)), 145 Err(e) => Err(MLError::new(ErrorType::Other, e)),
116 }?.url; 146 }?
147 .url;
117 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?; 148 userlist_change_versions(config, list.id, current_str, versions.join("|"), link, id)?;
118 } 149 }
119 150
120 if current.is_empty() { return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE")) }; 151 if current.is_empty() {
121 152 return Err(MLError::new(ErrorType::ModError, "NO_UPDATE_AVAILABLE"));
153 };
154
122 //println!(" └✔️"); 155 //println!(" └✔️");
123 Ok(current[0].clone()) 156 Ok(current[0].clone())
124} 157}
125 158
126#[tokio::test] 159#[tokio::test]
127async fn download_updates_test() { 160async fn download_updates_test() {
161 use crate::{
162 modrinth::{Hash, Version, VersionFile, VersionType},
163 List, Modloader,
164 };
128 165
129 use crate::{modrinth::{Version, VersionFile, Hash, VersionType}, Modloader, List};
130
131 let config = Cfg::init("modlist.toml").unwrap(); 166 let config = Cfg::init("modlist.toml").unwrap();
132 let current_list = List { id: String::from("..."), mc_version: String::from("..."), modloader: Modloader::Fabric, download_folder: String::from("./dev/tests/dl") }; 167 let current_list = List {
133 168 id: String::from("..."),
134 let versions = vec![Version { 169 mc_version: String::from("..."),
170 modloader: Modloader::Fabric,
171 download_folder: String::from("./dev/tests/dl"),
172 };
173
174 let versions = vec![Version {
135 id: "dEqtGnT9".to_string(), 175 id: "dEqtGnT9".to_string(),
136 project_id: "kYuIpRLv".to_string(), 176 project_id: "kYuIpRLv".to_string(),
137 author_id: "Qnt13hO8".to_string(), 177 author_id: "Qnt13hO8".to_string(),
@@ -161,5 +201,7 @@ async fn download_updates_test() {
161 "fabric".to_string() 201 "fabric".to_string()
162 ] 202 ]
163 }]; 203 }];
164 assert!(download_versions(current_list, config, versions).await.is_ok()) 204 assert!(download_versions(current_list, config, versions)
205 .await
206 .is_ok())
165} 207}
diff --git a/src/config.rs b/src/config.rs
index ded0062..1b54d5f 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,8 +1,11 @@
1use std::{fs::File, io::{Read, Write}}; 1use std::{
2 fs::File,
3 io::{Read, Write},
4};
2 5
3use serde::{Serialize, Deserialize}; 6use serde::{Deserialize, Serialize};
4 7
5use crate::{error::MLE, devdir}; 8use crate::{devdir, error::MLE};
6 9
7#[derive(Debug, Clone, Serialize, Deserialize)] 10#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Cfg { 11pub struct Cfg {
@@ -24,10 +27,15 @@ impl Cfg {
24 Err(err) => { 27 Err(err) => {
25 if err.kind() == std::io::ErrorKind::NotFound { 28 if err.kind() == std::io::ErrorKind::NotFound {
26 println!("No config file found, creating one"); 29 println!("No config file found, creating one");
27 let default_cfg = Cfg { data: String::from("./"), apis: Apis { modrinth: String::from("https://api.modrinth.com/v2/") } }; 30 let default_cfg = Cfg {
31 data: String::from("./"),
32 apis: Apis {
33 modrinth: String::from("https://api.modrinth.com/v2/"),
34 },
35 };
28 let mut file = File::create(devdir(configfile.to_str().unwrap()))?; 36 let mut file = File::create(devdir(configfile.to_str().unwrap()))?;
29 println!("Created config file"); 37 println!("Created config file");
30 file.write_all(&toml::to_string(&default_cfg)?.as_bytes())?; 38 file.write_all(toml::to_string(&default_cfg)?.as_bytes())?;
31 File::open(devdir(configfile.to_str().unwrap()))? 39 File::open(devdir(configfile.to_str().unwrap()))?
32 } else { 40 } else {
33 return Err(err.into()); 41 return Err(err.into());
diff --git a/src/db.rs b/src/db.rs
index 9428466..09d54c2 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -2,17 +2,21 @@ use std::io::{Error, ErrorKind};
2 2
3use rusqlite::Connection; 3use rusqlite::Connection;
4 4
5use crate::{Modloader, config::Cfg, List, devdir, error::{MLE, MLError, ErrorType}}; 5use crate::{
6 config::Cfg,
7 devdir,
8 error::{ErrorType, MLError, MLE},
9 List, Modloader,
10};
6 11
7//MODS 12//MODS
8pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> { 13pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> {
9
10 let data = devdir(format!("{}/data.db", config.data).as_str()); 14 let data = devdir(format!("{}/data.db", config.data).as_str());
11 let connection = Connection::open(data)?; 15 let connection = Connection::open(data)?;
12 16
13 connection.execute( 17 connection.execute(
14 "INSERT INTO mods (id, slug, title) VALUES (?1, ?2, ?3)", 18 "INSERT INTO mods (id, slug, title) VALUES (?1, ?2, ?3)",
15 [id, slug, name.replace('\'', "").as_str()] 19 [id, slug, name.replace('\'', "").as_str()],
16 )?; 20 )?;
17 21
18 Ok(()) 22 Ok(())
@@ -21,13 +25,11 @@ pub fn mods_insert(config: Cfg, id: &str, slug: &str, name: &str) -> MLE<()> {
21pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>> { 25pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::Error>> {
22 let data = devdir(format!("{}/data.db", config.data).as_str()); 26 let data = devdir(format!("{}/data.db", config.data).as_str());
23 let connection = Connection::open(data).unwrap(); 27 let connection = Connection::open(data).unwrap();
24 28
25 let mut mods: Vec<String> = Vec::new(); 29 let mut mods: Vec<String> = Vec::new();
26 30
27 let mut stmt = connection.prepare("SELECT id FROM mods")?; 31 let mut stmt = connection.prepare("SELECT id FROM mods")?;
28 let id_iter = stmt.query_map([], |row| { 32 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
29 row.get::<usize, String>(0)
30 })?;
31 33
32 for id in id_iter { 34 for id in id_iter {
33 mods.push(id?); 35 mods.push(id?);
@@ -49,33 +51,33 @@ pub fn mods_get_all_ids(config: Cfg) -> Result<Vec<String>, Box<dyn std::error::
49/// 51///
50///Will return `MLError` when no mod id is found 52///Will return `MLError` when no mod id is found
51pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> { 53pub fn mods_get_id(data: &str, slug: &str) -> MLE<String> {
54 //TODO check if "slug" is id
55
52 let data = devdir(format!("{}/data.db", data).as_str()); 56 let data = devdir(format!("{}/data.db", data).as_str());
53 let connection = Connection::open(data)?; 57 let connection = Connection::open(data)?;
54 58
55 let mut mod_id = String::new(); 59 let mut mod_id = String::new();
56 60
57 //get from slug 61 //get from slug
58 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?; 62 let mut stmt = connection.prepare("SELECT id FROM mods WHERE slug = ?")?;
59 let id_iter = stmt.query_map([slug], |row| { 63 let id_iter = stmt.query_map([slug], |row| row.get::<usize, String>(0))?;
60 row.get::<usize, String>(0)
61 })?;
62 64
63 for id in id_iter { 65 for id in id_iter {
64 mod_id = id?; 66 mod_id = id?;
65 }; 67 }
66 //get from title if no id found from slug 68 //get from title if no id found from slug
67 if mod_id.is_empty() { 69 if mod_id.is_empty() {
68 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?; 70 let mut stmt = connection.prepare("SELECT id FROM mods WHERE title = ?")?;
69 let id_iter = stmt.query_map([slug], |row| { 71 let id_iter = stmt.query_map([slug], |row| row.get::<usize, String>(0))?;
70 row.get::<usize, String>(0)
71 })?;
72 72
73 for id in id_iter { 73 for id in id_iter {
74 mod_id = id?; 74 mod_id = id?;
75 }; 75 }
76 } 76 }
77 77
78 if mod_id.is_empty() { return Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND")) }; 78 if mod_id.is_empty() {
79 return Err(MLError::new(ErrorType::DBError, "GI_MOD_NOT_FOUND"));
80 };
79 81
80 Ok(mod_id) 82 Ok(mod_id)
81} 83}
@@ -88,17 +90,23 @@ pub struct ModInfo {
88pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> { 90pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> {
89 let data = devdir(format!("{}/data.db", config.data).as_str()); 91 let data = devdir(format!("{}/data.db", config.data).as_str());
90 let connection = Connection::open(data)?; 92 let connection = Connection::open(data)?;
91 93
92 let mut mod_info: Option<ModInfo> = None; 94 let mut mod_info: Option<ModInfo> = None;
93 let mut stmt = connection.prepare("SELECT title, slug FROM mods WHERE id = ?")?; 95 let mut stmt = connection.prepare("SELECT title, slug FROM mods WHERE id = ?")?;
94 let name_iter = stmt.query_map([id], |row| { 96 let name_iter = stmt.query_map([id], |row| {
95 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 97 Ok(vec![
98 row.get::<usize, String>(0)?,
99 row.get::<usize, String>(1)?,
100 ])
96 })?; 101 })?;
97 102
98 for info in name_iter { 103 for info in name_iter {
99 let i = info?; 104 let i = info?;
100 mod_info = Some(ModInfo { title: String::from(&i[0]), slug: String::from(&i[1]) }); 105 mod_info = Some(ModInfo {
101 }; 106 title: String::from(&i[0]),
107 slug: String::from(&i[1]),
108 });
109 }
102 110
103 match mod_info.is_none() { 111 match mod_info.is_none() {
104 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")), 112 true => Err(MLError::new(ErrorType::DBError, "GN_MOD_NOT_FOUND")),
@@ -107,7 +115,6 @@ pub fn mods_get_info(config: Cfg, id: &str) -> MLE<ModInfo> {
107} 115}
108 116
109pub fn mods_remove(config: Cfg, id: String) -> MLE<()> { 117pub fn mods_remove(config: Cfg, id: String) -> MLE<()> {
110
111 println!("Removing mod {} from database", id); 118 println!("Removing mod {} from database", id);
112 119
113 let data = devdir(format!("{}/data.db", config.data).as_str()); 120 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -128,27 +135,42 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
128 let data = devdir(format!("{}/data.db", config.data).as_str()); 135 let data = devdir(format!("{}/data.db", config.data).as_str());
129 let connection = Connection::open(data)?; 136 let connection = Connection::open(data)?;
130 137
131 if mods.is_empty() { return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_INPUT")); } 138 if mods.is_empty() {
139 return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_INPUT"));
140 }
132 141
133 let mut wherestr = String::from("WHERE"); 142 let mut wherestr = String::from("WHERE");
134 for (i, id) in mods.iter().enumerate() { 143 for (i, id) in mods.iter().enumerate() {
135 let mut or = " OR"; 144 let mut or = " OR";
136 if i == mods.len() - 1 { or = "" }; 145 if i == mods.len() - 1 {
146 or = ""
147 };
137 wherestr = format!("{} id = '{}'{}", wherestr, id, or); 148 wherestr = format!("{} id = '{}'{}", wherestr, id, or);
138 } 149 }
139 150
140 let mut versionmaps: Vec<DBModlistVersions> = Vec::new(); 151 let mut versionmaps: Vec<DBModlistVersions> = Vec::new();
141 let mut stmt = connection.prepare(format!("SELECT id, versions, title FROM mods {}", wherestr).as_str())?; 152 let mut stmt = connection
153 .prepare(format!("SELECT id, versions, title FROM mods {}", wherestr).as_str())?;
142 let id_iter = stmt.query_map([], |row| { 154 let id_iter = stmt.query_map([], |row| {
143 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?]) 155 Ok(vec![
156 row.get::<usize, String>(0)?,
157 row.get::<usize, String>(1)?,
158 row.get::<usize, String>(2)?,
159 ])
144 })?; 160 })?;
145 161
146 for ver in id_iter { 162 for ver in id_iter {
147 let version = ver?; 163 let version = ver?;
148 println!("\t({}) Get versions from the database", String::from(&version[2])); 164 println!(
165 "\t({}) Get versions from the database",
166 String::from(&version[2])
167 );
149 //println!("Found versions {} for mod {}", version[1], version[0]); 168 //println!("Found versions {} for mod {}", version[1], version[0]);
150 versionmaps.push(DBModlistVersions { mod_id: String::from(&version[0]), versions: String::from(&version[1]) }) 169 versionmaps.push(DBModlistVersions {
151 }; 170 mod_id: String::from(&version[0]),
171 versions: String::from(&version[1]),
172 })
173 }
152 174
153 match versionmaps.is_empty() { 175 match versionmaps.is_empty() {
154 true => Err(MLError::new(ErrorType::DBError, "MODS_MODS_NOT_FOUND")), 176 true => Err(MLError::new(ErrorType::DBError, "MODS_MODS_NOT_FOUND")),
@@ -157,16 +179,37 @@ pub fn mods_get_versions(config: Cfg, mods: Vec<String>) -> MLE<Vec<DBModlistVer
157} 179}
158 180
159//userlist 181//userlist
160pub 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<()> { 182pub fn userlist_insert(
183 config: Cfg,
184 list_id: &str,
185 mod_id: &str,
186 current_version: &str,
187 applicable_versions: Vec<String>,
188 current_link: &str,
189 set_version: bool,
190) -> MLE<()> {
161 let data = devdir(format!("{}/data.db", config.data).as_str()); 191 let data = devdir(format!("{}/data.db", config.data).as_str());
162 let connection = Connection::open(data)?; 192 let connection = Connection::open(data)?;
163 193
164 let sv = match set_version { 194 let sv = match set_version {
165 true => "1", 195 true => "1",
166 false => "0", 196 false => "0",
167 }; 197 };
168 198
169 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])?; 199 connection.execute(
200 format!(
201 "INSERT INTO {} VALUES (?1, ?2, ?3, ?4, 'NONE', ?5)",
202 list_id
203 )
204 .as_str(),
205 [
206 mod_id,
207 current_version,
208 applicable_versions.join("|").as_str(),
209 current_link,
210 sv,
211 ],
212 )?;
170 213
171 Ok(()) 214 Ok(())
172} 215}
@@ -177,14 +220,12 @@ pub fn userlist_get_all_ids(config: Cfg, list_id: String) -> MLE<Vec<String>> {
177 220
178 let mut mod_ids: Vec<String> = Vec::new(); 221 let mut mod_ids: Vec<String> = Vec::new();
179 let mut stmt = connection.prepare(format!("SELECT mod_id FROM {}", list_id).as_str())?; 222 let mut stmt = connection.prepare(format!("SELECT mod_id FROM {}", list_id).as_str())?;
180 let id_iter = stmt.query_map([], |row| { 223 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
181 row.get::<usize, String>(0)
182 })?;
183 224
184 for id in id_iter { 225 for id in id_iter {
185 //println!("Found id {:?}", id.as_ref().unwrap()); 226 //println!("Found id {:?}", id.as_ref().unwrap());
186 mod_ids.push(id?) 227 mod_ids.push(id?)
187 }; 228 }
188 229
189 match mod_ids.is_empty() { 230 match mod_ids.is_empty() {
190 true => Err(MLError::new(ErrorType::DBError, "NO_MODS")), 231 true => Err(MLError::new(ErrorType::DBError, "NO_MODS")),
@@ -192,29 +233,38 @@ pub fn userlist_get_all_ids(config: Cfg, list_id: String) -> MLE<Vec<String>> {
192 } 233 }
193} 234}
194 235
195 236pub fn userlist_remove(config: Cfg, list_id: &str, mod_id: &str) -> MLE<()> {
196pub fn userlist_remove(config: Cfg, list_id: String, mod_id: String) -> MLE<()> {
197 let data = devdir(format!("{}/data.db", config.data).as_str()); 237 let data = devdir(format!("{}/data.db", config.data).as_str());
198 let connection = Connection::open(data)?; 238 let connection = Connection::open(data)?;
199 239
200 connection.execute(format!("DELETE FROM {} WHERE mod_id = ?", list_id).as_str(), [mod_id])?; 240 connection.execute(
241 format!("DELETE FROM {} WHERE mod_id = ?", list_id).as_str(),
242 [mod_id],
243 )?;
201 Ok(()) 244 Ok(())
202} 245}
203 246
204 247pub fn userlist_get_applicable_versions(
205pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: String) -> MLE<String> { 248 config: Cfg,
249 list_id: String,
250 mod_id: String,
251) -> MLE<String> {
206 let data = devdir(format!("{}/data.db", config.data).as_str()); 252 let data = devdir(format!("{}/data.db", config.data).as_str());
207 let connection = Connection::open(data).unwrap(); 253 let connection = Connection::open(data).unwrap();
208 254
209 let mut version: String = String::new(); 255 let mut version: String = String::new();
210 let mut stmt = connection.prepare(format!("SELECT applicable_versions FROM {} WHERE mod_id = ?", list_id).as_str())?; 256 let mut stmt = connection.prepare(
211 let ver_iter = stmt.query_map([mod_id], |row| { 257 format!(
212 row.get::<usize, String>(0) 258 "SELECT applicable_versions FROM {} WHERE mod_id = ?",
213 })?; 259 list_id
260 )
261 .as_str(),
262 )?;
263 let ver_iter = stmt.query_map([mod_id], |row| row.get::<usize, String>(0))?;
214 264
215 for ver in ver_iter { 265 for ver in ver_iter {
216 version = ver?; 266 version = ver?;
217 }; 267 }
218 268
219 match version.is_empty() { 269 match version.is_empty() {
220 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")), 270 true => Err(MLError::new(ErrorType::DBError, "GAV_MOD_NOT_FOUND")),
@@ -222,39 +272,47 @@ pub fn userlist_get_applicable_versions(config: Cfg, list_id: String, mod_id: St
222 } 272 }
223} 273}
224 274
225pub fn userlist_get_all_applicable_versions_with_mods(config: Cfg, list_id: String) -> MLE<Vec<(String, String)>> { 275pub fn userlist_get_all_applicable_versions_with_mods(
276 config: Cfg,
277 list_id: String,
278) -> MLE<Vec<(String, String)>> {
226 let data = devdir(format!("{}/data.db", config.data).as_str()); 279 let data = devdir(format!("{}/data.db", config.data).as_str());
227 let connection = Connection::open(data)?; 280 let connection = Connection::open(data)?;
228 281
229 let mut versions: Vec<(String, String)> = Vec::new(); 282 let mut versions: Vec<(String, String)> = Vec::new();
230 let mut stmt = connection.prepare(format!("SELECT mod_id, applicable_versions FROM {}", list_id).as_str())?; 283 let mut stmt = connection
284 .prepare(format!("SELECT mod_id, applicable_versions FROM {}", list_id).as_str())?;
231 let id_iter = stmt.query_map([], |row| { 285 let id_iter = stmt.query_map([], |row| {
232 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 286 Ok(vec![
287 row.get::<usize, String>(0)?,
288 row.get::<usize, String>(1)?,
289 ])
233 })?; 290 })?;
234 291
235 for ver in id_iter { 292 for ver in id_iter {
236 let out = ver?; 293 let out = ver?;
237 versions.push((out[0].to_owned(), out[1].to_owned())); 294 versions.push((out[0].to_owned(), out[1].to_owned()));
238 }; 295 }
239 296
240 if versions.is_empty() { return Err(MLError::new(ErrorType::DBError, "NO_MODS_ON_LIST")); }; 297 if versions.is_empty() {
298 return Err(MLError::new(ErrorType::DBError, "NO_MODS_ON_LIST"));
299 };
241 300
242 Ok(versions) 301 Ok(versions)
243} 302}
244 303
245pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String) -> MLE<String> { 304pub fn userlist_get_current_version(config: Cfg, list_id: &str, mod_id: &str) -> MLE<String> {
246 let data = devdir(format!("{}/data.db", config.data).as_str()); 305 let data = devdir(format!("{}/data.db", config.data).as_str());
247 let connection = Connection::open(data).unwrap(); 306 let connection = Connection::open(data).unwrap();
248 307
249 let mut version: String = String::new(); 308 let mut version: String = String::new();
250 let mut stmt = connection.prepare(format!("SELECT current_version FROM {} WHERE mod_id = ?", list_id).as_str())?; 309 let mut stmt = connection
251 let ver_iter = stmt.query_map([&mod_id], |row| { 310 .prepare(format!("SELECT current_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
252 row.get::<usize, String>(0) 311 let ver_iter = stmt.query_map([&mod_id], |row| row.get::<usize, String>(0))?;
253 })?;
254 312
255 for ver in ver_iter { 313 for ver in ver_iter {
256 version = ver?; 314 version = ver?;
257 }; 315 }
258 316
259 match version.is_empty() { 317 match version.is_empty() {
260 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")), 318 true => Err(MLError::new(ErrorType::DBError, "GCV_MOD_NOT_FOUND")),
@@ -262,63 +320,88 @@ pub fn userlist_get_current_version(config: Cfg, list_id: String, mod_id: String
262 } 320 }
263} 321}
264 322
265pub fn userlist_get_all_current_version_ids(config: Cfg, list_id: String) -> Result<Vec<String>, Box<dyn std::error::Error>> { 323pub fn userlist_get_all_current_version_ids(
324 config: Cfg,
325 list_id: String,
326) -> Result<Vec<String>, Box<dyn std::error::Error>> {
266 let data = devdir(format!("{}/data.db", config.data).as_str()); 327 let data = devdir(format!("{}/data.db", config.data).as_str());
267 let connection = Connection::open(data)?; 328 let connection = Connection::open(data)?;
268 329
269 let mut versions: Vec<String> = Vec::new(); 330 let mut versions: Vec<String> = Vec::new();
270 let mut stmt = connection.prepare(format!("SELECT current_version FROM {}", list_id).as_str())?; 331 let mut stmt =
271 let id_iter = stmt.query_map([], |row| { 332 connection.prepare(format!("SELECT current_version FROM {}", list_id).as_str())?;
272 row.get::<usize, String>(0) 333 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
273 })?;
274 334
275 for id in id_iter { 335 for id in id_iter {
276 versions.push(id?); 336 versions.push(id?);
277 }; 337 }
278 338
279 if versions.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; 339 if versions.is_empty() {
340 return Err(Box::new(std::io::Error::new(
341 ErrorKind::Other,
342 "NO_MODS_ON_LIST",
343 )));
344 };
280 345
281 Ok(versions) 346 Ok(versions)
282} 347}
283 348
284pub fn userlist_get_all_current_versions_with_mods(config: Cfg, list_id: String) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> { 349pub fn userlist_get_all_current_versions_with_mods(
350 config: Cfg,
351 list_id: String,
352) -> Result<Vec<(String, String)>, Box<dyn std::error::Error>> {
285 let data = devdir(format!("{}/data.db", config.data).as_str()); 353 let data = devdir(format!("{}/data.db", config.data).as_str());
286 let connection = Connection::open(data)?; 354 let connection = Connection::open(data)?;
287 355
288 let mut versions: Vec<(String, String)> = Vec::new(); 356 let mut versions: Vec<(String, String)> = Vec::new();
289 let mut stmt = connection.prepare(format!("SELECT mod_id, current_version FROM {}", list_id).as_str())?; 357 let mut stmt =
358 connection.prepare(format!("SELECT mod_id, current_version FROM {}", list_id).as_str())?;
290 let id_iter = stmt.query_map([], |row| { 359 let id_iter = stmt.query_map([], |row| {
291 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?]) 360 Ok(vec![
361 row.get::<usize, String>(0)?,
362 row.get::<usize, String>(1)?,
363 ])
292 })?; 364 })?;
293 365
294 for ver in id_iter { 366 for ver in id_iter {
295 let out = ver?; 367 let out = ver?;
296 versions.push((out[0].to_owned(), out[1].to_owned())); 368 versions.push((out[0].to_owned(), out[1].to_owned()));
297 }; 369 }
298 370
299 if versions.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; 371 if versions.is_empty() {
372 return Err(Box::new(std::io::Error::new(
373 ErrorKind::Other,
374 "NO_MODS_ON_LIST",
375 )));
376 };
300 377
301 Ok(versions) 378 Ok(versions)
302} 379}
303 380
304pub fn userlist_get_set_version(config:Cfg, list_id: &str, mod_id: &str) -> MLE<bool> { 381pub fn userlist_get_set_version(config: Cfg, list_id: &str, mod_id: &str) -> MLE<bool> {
305 let data = devdir(format!("{}/data.db", config.data).as_str()); 382 let data = devdir(format!("{}/data.db", config.data).as_str());
306 let connection = Connection::open(data).unwrap(); 383 let connection = Connection::open(data).unwrap();
307 384
308 let mut set_version: bool = false; 385 let mut set_version: bool = false;
309 let mut stmt = connection.prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?; 386 let mut stmt = connection
310 let ver_iter = stmt.query_map([&mod_id], |row| { 387 .prepare(format!("SELECT set_version FROM {} WHERE mod_id = ?", list_id).as_str())?;
311 row.get::<usize, bool>(0) 388 let ver_iter = stmt.query_map([&mod_id], |row| row.get::<usize, bool>(0))?;
312 })?;
313 389
314 for ver in ver_iter { 390 for ver in ver_iter {
315 set_version = ver?; 391 set_version = ver?;
316 }; 392 }
317 393
318 Ok(set_version) 394 Ok(set_version)
319} 395}
320 396
321pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: String, versions: String, link: String, mod_id: String) -> MLE<()> { 397pub fn userlist_change_versions(
398 config: Cfg,
399 list_id: String,
400 current_version: String,
401 versions: String,
402 link: String,
403 mod_id: String,
404) -> MLE<()> {
322 let data = devdir(format!("{}/data.db", config.data).as_str()); 405 let data = devdir(format!("{}/data.db", config.data).as_str());
323 let connection = Connection::open(data)?; 406 let connection = Connection::open(data)?;
324 407
@@ -326,33 +409,45 @@ pub fn userlist_change_versions(config: Cfg, list_id: String, current_version: S
326 Ok(()) 409 Ok(())
327} 410}
328 411
329pub fn userlist_add_disabled_versions(config: Cfg, list_id: String, disabled_version: String, mod_id: String) -> MLE<()> { 412pub fn userlist_add_disabled_versions(
413 config: Cfg,
414 list_id: String,
415 disabled_version: String,
416 mod_id: String,
417) -> MLE<()> {
330 let data = devdir(format!("{}/data.db", config.data).as_str()); 418 let data = devdir(format!("{}/data.db", config.data).as_str());
331 let connection = Connection::open(data)?; 419 let connection = Connection::open(data)?;
332 420
333 let currently_disabled_versions = userlist_get_disabled_versions(config, String::from(&list_id), String::from(&mod_id))?; 421 let currently_disabled_versions =
334 let disabled_versions = match currently_disabled_versions == "NONE" { 422 userlist_get_disabled_versions(config, String::from(&list_id), String::from(&mod_id))?;
423 let disabled_versions = match currently_disabled_versions == "NONE" {
335 true => disabled_version, 424 true => disabled_version,
336 false => format!("{}|{}", currently_disabled_versions, disabled_version), 425 false => format!("{}|{}", currently_disabled_versions, disabled_version),
337 }; 426 };
338 427
339 connection.execute(format!("UPDATE {} SET disabled_versions = ?1 WHERE mod_id = ?2", list_id).as_str(), [disabled_versions, mod_id])?; 428 connection.execute(
429 format!(
430 "UPDATE {} SET disabled_versions = ?1 WHERE mod_id = ?2",
431 list_id
432 )
433 .as_str(),
434 [disabled_versions, mod_id],
435 )?;
340 Ok(()) 436 Ok(())
341} 437}
342 438
343pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: String) -> MLE<String> { 439pub fn userlist_get_disabled_versions(config: Cfg, list_id: String, mod_id: String) -> MLE<String> {
344 let data = devdir(format!("{}/data.db", config.data).as_str()); 440 let data = devdir(format!("{}/data.db", config.data).as_str());
345 let connection = Connection::open(data).unwrap(); 441 let connection = Connection::open(data).unwrap();
346 442
347 let mut version: String = String::new(); 443 let mut version: String = String::new();
348 let mut stmt = connection.prepare(format!("SELECT disabled_versions FROM {} WHERE mod_id = ?", list_id).as_str())?; 444 let mut stmt = connection
349 let ver_iter = stmt.query_map([mod_id], |row| { 445 .prepare(format!("SELECT disabled_versions FROM {} WHERE mod_id = ?", list_id).as_str())?;
350 row.get::<usize, String>(0) 446 let ver_iter = stmt.query_map([mod_id], |row| row.get::<usize, String>(0))?;
351 })?;
352 447
353 for ver in ver_iter { 448 for ver in ver_iter {
354 version = ver?; 449 version = ver?;
355 }; 450 }
356 451
357 match version.is_empty() { 452 match version.is_empty() {
358 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")), 453 true => Err(MLError::new(ErrorType::DBError, "GDV_MOD_NOT_FOUND")),
@@ -360,36 +455,57 @@ pub fn userlist_get_disabled_versions(config:Cfg, list_id: String, mod_id: Strin
360 } 455 }
361} 456}
362 457
363pub fn userlist_get_all_downloads(config: Cfg, list_id: String) -> Result<Vec<String>, Box<dyn std::error::Error>> { 458pub fn userlist_get_all_downloads(
459 config: Cfg,
460 list_id: String,
461) -> Result<Vec<String>, Box<dyn std::error::Error>> {
364 let data = devdir(format!("{}/data.db", config.data).as_str()); 462 let data = devdir(format!("{}/data.db", config.data).as_str());
365 let connection = Connection::open(data).unwrap(); 463 let connection = Connection::open(data).unwrap();
366 464
367 let mut links: Vec<String> = Vec::new(); 465 let mut links: Vec<String> = Vec::new();
368 let mut stmt = connection.prepare(format!("SELECT current_download FROM {}", list_id).as_str())?; 466 let mut stmt =
369 let link_iter = stmt.query_map([], |row| { 467 connection.prepare(format!("SELECT current_download FROM {}", list_id).as_str())?;
370 row.get::<usize, String>(0) 468 let link_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
371 })?;
372 469
373 for link in link_iter { 470 for link in link_iter {
374 let l = link?; 471 let l = link?;
375 println!("Found link {}", String::from(&l)); 472 println!("Found link {}", String::from(&l));
376 links.push(l) 473 links.push(l)
377 }; 474 }
378 475
379 if links.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_MODS_ON_LIST"))); }; 476 if links.is_empty() {
477 return Err(Box::new(std::io::Error::new(
478 ErrorKind::Other,
479 "NO_MODS_ON_LIST",
480 )));
481 };
380 482
381 Ok(links) 483 Ok(links)
382} 484}
383 485
384//lists 486//lists
385///Inserts into lists table and creates new table 487///Inserts into lists table and creates new table
386pub fn lists_insert(config: Cfg, id: String, mc_version: String, mod_loader: Modloader, download_folder: String) -> MLE<()> { 488pub fn lists_insert(
489 config: Cfg,
490 id: String,
491 mc_version: String,
492 mod_loader: Modloader,
493 download_folder: String,
494) -> MLE<()> {
387 println!("Creating list {}", id); 495 println!("Creating list {}", id);
388 496
389 let data = devdir(format!("{}/data.db", config.data).as_str()); 497 let data = devdir(format!("{}/data.db", config.data).as_str());
390 let connection = Connection::open(data)?; 498 let connection = Connection::open(data)?;
391 499
392 connection.execute("INSERT INTO lists VALUES (?1, ?2, ?3, ?4)", [id.clone(), mc_version, mod_loader.to_string(), download_folder])?; 500 connection.execute(
501 "INSERT INTO lists VALUES (?1, ?2, ?3, ?4)",
502 [
503 id.clone(),
504 mc_version,
505 mod_loader.to_string(),
506 download_folder,
507 ],
508 )?;
393 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(), [])?; 509 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(), [])?;
394 510
395 Ok(()) 511 Ok(())
@@ -408,44 +524,62 @@ pub fn lists_get(config: Cfg, list_id: String) -> MLE<List> {
408 let data = devdir(format!("{}/data.db", config.data).as_str()); 524 let data = devdir(format!("{}/data.db", config.data).as_str());
409 let connection = Connection::open(data).unwrap(); 525 let connection = Connection::open(data).unwrap();
410 526
411 let mut list = List { id: String::new(), mc_version: String::new(), modloader: Modloader::Fabric, download_folder: String::new() }; 527 let mut list = List {
412 let mut stmt = connection.prepare("SELECT mc_version, modloader, download_folder FROM lists WHERE id = ?")?; 528 id: String::new(),
529 mc_version: String::new(),
530 modloader: Modloader::Fabric,
531 download_folder: String::new(),
532 };
533 let mut stmt = connection
534 .prepare("SELECT mc_version, modloader, download_folder FROM lists WHERE id = ?")?;
413 535
414 let list_iter = stmt.query_map([&list_id], |row| { 536 let list_iter = stmt.query_map([&list_id], |row| {
415 Ok(vec![row.get::<usize, String>(0)?, row.get::<usize, String>(1)?, row.get::<usize, String>(2)?]) 537 Ok(vec![
538 row.get::<usize, String>(0)?,
539 row.get::<usize, String>(1)?,
540 row.get::<usize, String>(2)?,
541 ])
416 })?; 542 })?;
417 543
418 for l in list_iter { 544 for l in list_iter {
419 let li = l?; 545 let li = l?;
420 list = List { id: String::from(&list_id), mc_version: String::from(&li[0]), modloader: Modloader::from(&li[1])?, download_folder: String::from(&li[2]) }; 546 list = List {
421 }; 547 id: String::from(&list_id),
548 mc_version: String::from(&li[0]),
549 modloader: Modloader::from(&li[1])?,
550 download_folder: String::from(&li[2]),
551 };
552 }
553
554 if list.id.is_empty() {
555 return Err(MLError::new(ErrorType::DBError, "LIST_NOT_FOUND"));
556 }
422 557
423 if list.id.is_empty() { return Err(MLError::new(ErrorType::DBError, "LIST_NOT_FOUND")); }
424
425 Ok(list) 558 Ok(list)
426} 559}
427 560
428pub fn lists_version(config: Cfg, list_id: String, version: String) -> MLE<()> { 561pub fn lists_version(config: Cfg, list_id: &str, version: &str) -> MLE<()> {
429 let data = devdir(format!("{}/data.db", config.data).as_str()); 562 let data = devdir(format!("{}/data.db", config.data).as_str());
430 let connection = Connection::open(data).unwrap(); 563 let connection = Connection::open(data).unwrap();
431 564
432 connection.execute("UPDATE lists SET mc_version = ? WHERE id = ?", [version, list_id])?; 565 connection.execute(
566 "UPDATE lists SET mc_version = ? WHERE id = ?",
567 [version, list_id],
568 )?;
433 Ok(()) 569 Ok(())
434} 570}
435 571
436pub fn lists_get_all_ids(config: Cfg) -> MLE<Vec<String>> { 572pub fn lists_get_all_ids(config: Cfg) -> MLE<Vec<String>> {
437 let data = devdir(format!("{}/data.db", config.data).as_str()); 573 let data = devdir(format!("{}/data.db", config.data).as_str());
438 let connection = Connection::open(data).unwrap(); 574 let connection = Connection::open(data).unwrap();
439 575
440 let mut list_ids: Vec<String> = Vec::new(); 576 let mut list_ids: Vec<String> = Vec::new();
441 let mut stmt = connection.prepare("SELECT id FROM lists")?; 577 let mut stmt = connection.prepare("SELECT id FROM lists")?;
442 let id_iter = stmt.query_map([], |row| { 578 let id_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
443 row.get::<usize, String>(0)
444 })?;
445 579
446 for id in id_iter { 580 for id in id_iter {
447 list_ids.push(id?) 581 list_ids.push(id?)
448 }; 582 }
449 583
450 match list_ids.is_empty() { 584 match list_ids.is_empty() {
451 true => Err(MLError::new(ErrorType::DBError, "NO_LISTS")), 585 true => Err(MLError::new(ErrorType::DBError, "NO_LISTS")),
@@ -458,35 +592,50 @@ pub fn config_change_current_list(config: Cfg, id: String) -> MLE<()> {
458 let data = devdir(format!("{}/data.db", config.data).as_str()); 592 let data = devdir(format!("{}/data.db", config.data).as_str());
459 let connection = Connection::open(data)?; 593 let connection = Connection::open(data)?;
460 594
461 connection.execute("UPDATE user_config SET value = ? WHERE id = 'current_list'", [id])?; 595 connection.execute(
596 "UPDATE user_config SET value = ? WHERE id = 'current_list'",
597 [id],
598 )?;
462 Ok(()) 599 Ok(())
463} 600}
464 601
465pub fn config_get_current_list(config: Cfg) -> MLE<String> { 602pub fn config_get_current_list(config: Cfg) -> MLE<String> {
466 let data = devdir(format!("{}/data.db", config.data).as_str()); 603 let data = devdir(format!("{}/data.db", config.data).as_str());
467 let connection = Connection::open(data).unwrap(); 604 let connection = Connection::open(data).unwrap();
468 605
469 let mut list_id = String::new(); 606 let mut list_id = String::new();
470 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'current_list'")?; 607 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'current_list'")?;
471 let list_iter = stmt.query_map([], |row| { 608 let list_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
472 row.get::<usize, String>(0)
473 })?;
474 609
475 for list in list_iter { 610 for list in list_iter {
476 list_id = list?; 611 list_id = list?;
477 }; 612 }
613
614 if list_id.is_empty() {
615 return Err(MLError::new(ErrorType::DBError, "NO_CURRENT_LIST"));
616 }
478 617
479 if list_id.is_empty() { return Err(MLError::new(ErrorType::DBError, "NO_CURRENT_LIST")); }
480
481 Ok(list_id) 618 Ok(list_id)
482} 619}
483 620
484//SETUP(UPDATES) 621//SETUP(UPDATES)
485pub fn s_userlist_update_download(config: Cfg, list_id: String, mod_id: String, link: String) -> Result<(), Box<dyn std::error::Error>> { 622pub fn s_userlist_update_download(
623 config: Cfg,
624 list_id: String,
625 mod_id: String,
626 link: String,
627) -> Result<(), Box<dyn std::error::Error>> {
486 let data = devdir(format!("{}/data.db", config.data).as_str()); 628 let data = devdir(format!("{}/data.db", config.data).as_str());
487 let connection = Connection::open(data)?; 629 let connection = Connection::open(data)?;
488 630
489 connection.execute(format!("UPDATE {} SET current_download = ?1 WHERE mod_id = ?2", list_id).as_str(), [link, mod_id])?; 631 connection.execute(
632 format!(
633 "UPDATE {} SET current_download = ?1 WHERE mod_id = ?2",
634 list_id
635 )
636 .as_str(),
637 [link, mod_id],
638 )?;
490 Ok(()) 639 Ok(())
491} 640}
492 641
@@ -494,7 +643,10 @@ pub fn s_config_create_version(config: Cfg) -> Result<(), Box<dyn std::error::Er
494 let data = devdir(format!("{}/data.db", config.data).as_str()); 643 let data = devdir(format!("{}/data.db", config.data).as_str());
495 let connection = Connection::open(data)?; 644 let connection = Connection::open(data)?;
496 645
497 connection.execute("INSERT INTO 'user_config' VALUES ( 'db_version', '0.2' )", ())?; 646 connection.execute(
647 "INSERT INTO 'user_config' VALUES ( 'db_version', '0.2' )",
648 (),
649 )?;
498 Ok(()) 650 Ok(())
499} 651}
500 652
@@ -502,34 +654,46 @@ pub fn s_config_update_version(config: Cfg, ver: String) -> Result<(), Box<dyn s
502 let data = devdir(format!("{}/data.db", config.data).as_str()); 654 let data = devdir(format!("{}/data.db", config.data).as_str());
503 let connection = Connection::open(data)?; 655 let connection = Connection::open(data)?;
504 656
505 connection.execute("UPDATE user_config SET value = ? WHERE id = 'db_version'", [ver])?; 657 connection.execute(
658 "UPDATE user_config SET value = ? WHERE id = 'db_version'",
659 [ver],
660 )?;
506 Ok(()) 661 Ok(())
507} 662}
508 663
509pub fn s_config_get_version(config: Cfg) -> Result<String, Box<dyn std::error::Error>> { 664pub fn s_config_get_version(config: Cfg) -> Result<String, Box<dyn std::error::Error>> {
510 let data = devdir(format!("{}/data.db", config.data).as_str()); 665 let data = devdir(format!("{}/data.db", config.data).as_str());
511 let connection = Connection::open(data)?; 666 let connection = Connection::open(data)?;
512 667
513 let mut version: String = String::new(); 668 let mut version: String = String::new();
514 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'db_version'")?; 669 let mut stmt = connection.prepare("SELECT value FROM user_config WHERE id = 'db_version'")?;
515 let ver_iter = stmt.query_map([], |row| { 670 let ver_iter = stmt.query_map([], |row| row.get::<usize, String>(0))?;
516 row.get::<usize, String>(0)
517 })?;
518 671
519 for ver in ver_iter { 672 for ver in ver_iter {
520 version = ver?; 673 version = ver?;
521 }; 674 }
522 675
523 if version.is_empty() { return Err(Box::new(std::io::Error::new(ErrorKind::Other, "NO_DBVERSION"))); }; 676 if version.is_empty() {
677 return Err(Box::new(std::io::Error::new(
678 ErrorKind::Other,
679 "NO_DBVERSION",
680 )));
681 };
524 Ok(version) 682 Ok(version)
525} 683}
526 684
527pub fn s_insert_column(config: Cfg, table: String, column: String, c_type: String, default: Option<String>) -> Result<(), Box<dyn std::error::Error>> { 685pub fn s_insert_column(
686 config: Cfg,
687 table: String,
688 column: String,
689 c_type: String,
690 default: Option<String>,
691) -> Result<(), Box<dyn std::error::Error>> {
528 let data = devdir(format!("{}/data.db", config.data).as_str()); 692 let data = devdir(format!("{}/data.db", config.data).as_str());
529 let connection = Connection::open(data)?; 693 let connection = Connection::open(data)?;
530 694
531 let mut sql = format!("ALTER TABLE {} ADD '{}' {}", table, column, c_type); 695 let mut sql = format!("ALTER TABLE {} ADD '{}' {}", table, column, c_type);
532 696
533 if default.is_some() { 697 if default.is_some() {
534 sql = format!("{} DEFAULT {}", sql, default.unwrap()); 698 sql = format!("{} DEFAULT {}", sql, default.unwrap());
535 } 699 }
@@ -539,7 +703,6 @@ pub fn s_insert_column(config: Cfg, table: String, column: String, c_type: Strin
539} 703}
540 704
541pub fn db_setup(config: Cfg) -> MLE<()> { 705pub fn db_setup(config: Cfg) -> MLE<()> {
542
543 println!("Initiating database"); 706 println!("Initiating database");
544 707
545 let data = devdir(format!("{}/data.db", config.data).as_str()); 708 let data = devdir(format!("{}/data.db", config.data).as_str());
@@ -552,6 +715,6 @@ pub fn db_setup(config: Cfg) -> MLE<()> {
552 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' ); 715 INSERT INTO 'user_config' VALUES ( 'db_version', '0.5' );
553 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )", 716 INSERT INTO 'user_config' VALUES ( 'current_list', '...' )",
554 )?; 717 )?;
555 718
556 Ok(()) 719 Ok(())
557} 720}
diff --git a/src/error.rs b/src/error.rs
index 794a919..bd6e3da 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -43,49 +43,70 @@ impl fmt::Display for MLError {
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"),
46 ErrorType::Other => write!(f, "OTHER") 46 ErrorType::Other => write!(f, "OTHER"),
47 } 47 }
48 } 48 }
49} 49}
50 50
51impl From<reqwest::Error> for MLError { 51impl From<reqwest::Error> for MLError {
52 fn from(error: reqwest::Error) -> Self { 52 fn from(error: reqwest::Error) -> Self {
53 Self { etype: ErrorType::LibReq, message: error.to_string() } 53 Self {
54 etype: ErrorType::LibReq,
55 message: error.to_string(),
56 }
54 } 57 }
55} 58}
56 59
57impl From<toml::de::Error> for MLError { 60impl From<toml::de::Error> for MLError {
58 fn from(error: toml::de::Error) -> Self { 61 fn from(error: toml::de::Error) -> Self {
59 Self { etype: ErrorType::LibToml, message: error.to_string() } 62 Self {
63 etype: ErrorType::LibToml,
64 message: error.to_string(),
65 }
60 } 66 }
61} 67}
62 68
63impl From<rusqlite::Error> for MLError { 69impl From<rusqlite::Error> for MLError {
64 fn from(error: rusqlite::Error) -> Self { 70 fn from(error: rusqlite::Error) -> Self {
65 Self { etype: ErrorType::LibSql, message: error.to_string() } 71 Self {
72 etype: ErrorType::LibSql,
73 message: error.to_string(),
74 }
66 } 75 }
67} 76}
68 77
69impl From<toml::ser::Error> for MLError { 78impl From<toml::ser::Error> for MLError {
70 fn from(error: toml::ser::Error) -> Self { 79 fn from(error: toml::ser::Error) -> Self {
71 Self { etype: ErrorType::LibToml, message: error.to_string() } 80 Self {
81 etype: ErrorType::LibToml,
82 message: error.to_string(),
83 }
72 } 84 }
73} 85}
74 86
75impl From<chrono::ParseError> for MLError { 87impl From<chrono::ParseError> for MLError {
76 fn from(error: chrono::ParseError) -> Self { 88 fn from(error: chrono::ParseError) -> Self {
77 Self { etype: ErrorType::LibChrono, message: error.to_string() } 89 Self {
90 etype: ErrorType::LibChrono,
91 message: error.to_string(),
92 }
78 } 93 }
79} 94}
80 95
81impl From<std::io::Error> for MLError { 96impl From<std::io::Error> for MLError {
82 fn from(error: std::io::Error) -> Self { 97 fn from(error: std::io::Error) -> Self {
83 Self { etype: ErrorType::IoError, message: error.to_string() } 98 Self {
99 etype: ErrorType::IoError,
100 message: error.to_string(),
101 }
84 } 102 }
85} 103}
86 104
87impl MLError { 105impl MLError {
88 pub fn new(etype: ErrorType, message: &str) -> Self { 106 pub fn new(etype: ErrorType, message: &str) -> Self {
89 Self { etype, message: String::from(message) } 107 Self {
108 etype,
109 message: String::from(message),
110 }
90 } 111 }
91} 112}
diff --git a/src/files.rs b/src/files.rs
index 6519c6a..6160cb4 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -1,11 +1,20 @@
1use std::{fs::{File, read_dir, remove_file, rename}, io::Write, collections::HashMap};
2use futures_util::StreamExt; 1use futures_util::StreamExt;
3use reqwest::Client; 2use reqwest::Client;
4 3use std::{
5use crate::{List, modrinth::Version, db::{userlist_add_disabled_versions, mods_get_info}, config::Cfg, error::{MLE, MLError, ErrorType}}; 4 collections::HashMap,
5 fs::{read_dir, remove_file, rename, File},
6 io::Write,
7};
8
9use crate::{
10 config::Cfg,
11 db::{mods_get_info, userlist_add_disabled_versions},
12 error::{ErrorType, MLError, MLE},
13 modrinth::Version,
14 List,
15};
6 16
7pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> { 17pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>) -> MLE<String> {
8
9 let dl_path = String::from(&list.download_folder); 18 let dl_path = String::from(&list.download_folder);
10 19
11 println!(" └Download mods to {}", dl_path); 20 println!(" └Download mods to {}", dl_path);
@@ -21,7 +30,13 @@ pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>)
21 Ok(e) => e, 30 Ok(e) => e,
22 Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")), 31 Err(..) => return Err(MLError::new(ErrorType::Other, "NO_FILE_EXTENSION")),
23 }; 32 };
24 let filename = format!("{}.mr.{}.{}.{}", splitname.join("."), ver.project_id, ver.id, extension); 33 let filename = format!(
34 "{}.mr.{}.{}.{}",
35 splitname.join("."),
36 ver.project_id,
37 ver.id,
38 extension
39 );
25 download_file(primary_file.url, list.clone().download_folder, filename).await?; 40 download_file(primary_file.url, list.clone().download_folder, filename).await?;
26 //tokio::time::sleep(std::time::Duration::new(3, 0)).await; 41 //tokio::time::sleep(std::time::Duration::new(3, 0)).await;
27 println!(" ✓"); 42 println!(" ✓");
@@ -32,10 +47,7 @@ pub async fn download_versions(list: List, config: Cfg, versions: Vec<Version>)
32 47
33async fn download_file(url: String, path: String, name: String) -> MLE<()> { 48async fn download_file(url: String, path: String, name: String) -> MLE<()> {
34 let dl_path_file = format!("{}/{}", path, name); 49 let dl_path_file = format!("{}/{}", path, name);
35 let res = Client::new() 50 let res = Client::new().get(String::from(&url)).send().await?;
36 .get(String::from(&url))
37 .send()
38 .await?;
39 51
40 // download chunks 52 // download chunks
41 let mut file = File::create(&dl_path_file)?; 53 let mut file = File::create(&dl_path_file)?;
@@ -49,7 +61,12 @@ async fn download_file(url: String, path: String, name: String) -> MLE<()> {
49 Ok(()) 61 Ok(())
50} 62}
51 63
52pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_id: String) -> MLE<()> { 64pub fn disable_version(
65 config: Cfg,
66 current_list: List,
67 versionid: String,
68 mod_id: String,
69) -> MLE<()> {
53 //println!("Disabling version {} for mod {}", versionid, mod_id); 70 //println!("Disabling version {} for mod {}", versionid, mod_id);
54 let file = get_file_path(current_list.clone(), String::from(&versionid))?; 71 let file = get_file_path(current_list.clone(), String::from(&versionid))?;
55 let disabled = format!("{}.disabled", file); 72 let disabled = format!("{}.disabled", file);
@@ -63,7 +80,7 @@ pub fn disable_version(config: Cfg, current_list: List, versionid: String, mod_i
63 80
64pub fn delete_version(list: List, version: String) -> MLE<()> { 81pub fn delete_version(list: List, version: String) -> MLE<()> {
65 let file = get_file_path(list, version)?; 82 let file = get_file_path(list, version)?;
66 83
67 remove_file(file)?; 84 remove_file(file)?;
68 85
69 Ok(()) 86 Ok(())
@@ -76,19 +93,24 @@ pub fn get_file_path(list: List, versionid: String) -> MLE<String> {
76 if path.is_file() { 93 if path.is_file() {
77 let pathstr = match path.to_str().ok_or("") { 94 let pathstr = match path.to_str().ok_or("") {
78 Ok(s) => s, 95 Ok(s) => s,
79 Err(..) => return Err(MLError::new(ErrorType::Other, "INVALID_PATH")) 96 Err(..) => return Err(MLError::new(ErrorType::Other, "INVALID_PATH")),
80 }; 97 };
81 let namesplit: Vec<&str> = pathstr.split('.').collect(); 98 let namesplit: Vec<&str> = pathstr.split('.').collect();
82 let ver_id = namesplit[namesplit.len() - 2]; 99 let ver_id = namesplit[namesplit.len() - 2];
83 names.insert(String::from(ver_id), String::from(pathstr)); 100 names.insert(String::from(ver_id), String::from(pathstr));
84 } 101 }
85 }; 102 }
86 103
87 let filename = match names.get(&versionid).ok_or("") { 104 let filename = match names.get(&versionid).ok_or("") {
88 Ok(n) => n, 105 Ok(n) => n,
89 Err(..) => return Err(MLError::new(ErrorType::ArgumentError, "VERSION_NOT_FOUND_IN_FILES")) 106 Err(..) => {
107 return Err(MLError::new(
108 ErrorType::ArgumentError,
109 "VERSION_NOT_FOUND_IN_FILES",
110 ))
111 }
90 }; 112 };
91 113
92 Ok(filename.to_owned()) 114 Ok(filename.to_owned())
93} 115}
94 116
@@ -99,7 +121,10 @@ pub fn get_downloaded_versions(list: List) -> MLE<HashMap<String, String>> {
99 if path.is_file() && path.extension().ok_or("BAH").unwrap() == "jar" { 121 if path.is_file() && path.extension().ok_or("BAH").unwrap() == "jar" {
100 let pathstr = path.to_str().ok_or("BAH").unwrap(); 122 let pathstr = path.to_str().ok_or("BAH").unwrap();
101 let namesplit: Vec<&str> = pathstr.split('.').collect(); 123 let namesplit: Vec<&str> = pathstr.split('.').collect();
102 versions.insert(String::from(namesplit[namesplit.len() - 3]), String::from(namesplit[namesplit.len() - 2])); 124 versions.insert(
125 String::from(namesplit[namesplit.len() - 3]),
126 String::from(namesplit[namesplit.len() - 2]),
127 );
103 } 128 }
104 } 129 }
105 Ok(versions) 130 Ok(versions)
@@ -111,6 +136,6 @@ pub fn clean_list_dir(list: &List) -> MLE<()> {
111 for entry in std::fs::read_dir(dl_path)? { 136 for entry in std::fs::read_dir(dl_path)? {
112 let entry = entry?; 137 let entry = entry?;
113 std::fs::remove_file(entry.path())?; 138 std::fs::remove_file(entry.path())?;
114 }; 139 }
115 Ok(()) 140 Ok(())
116} 141}
diff --git a/src/input.rs b/src/input.rs
deleted file mode 100644
index 6c62ab7..0000000
--- a/src/input.rs
+++ /dev/null
@@ -1,344 +0,0 @@
1use crate::{error::{MLE, MLError, ErrorType}, Modloader, config::Cfg, db::lists_get, get_current_list, List, modrinth::{get_minecraft_version, MCVersionType}, IDSelector};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct Input {
5 pub command: Option<Cmd>,
6 pub mod_options: Option<ModOptions>,
7 pub mod_id: Option<IDSelector>,
8 pub set_version: bool,
9 pub all_lists: bool,
10 pub clean: bool,
11 pub direct_download: bool,
12 pub delete_old: bool,
13 pub list: Option<List>,
14 pub list_options: Option<ListOptions>,
15 pub list_id: Option<String>,
16 pub list_mcversion: Option<String>,
17 pub modloader: Option<Modloader>,
18 pub directory: Option<String>,
19 pub io_options: Option<IoOptions>,
20 pub file: Option<String>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum Cmd {
25 Mod,
26 List,
27 Update,
28 Download,
29 Io,
30 Version,
31 Setup,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum ModOptions {
36 Add,
37 Remove
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum ListOptions {
42 Add,
43 Remove,
44 Change,
45 Version,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum IoOptions {
50 Export,
51 Import
52}
53
54impl Input {
55 fn from(config: Cfg, input: Vec<String>) -> MLE<Self> {
56 let input_string = input.join(" ");
57 let mut args: Vec<&str> = input_string.split(" -").collect();
58 args[0] = args[0].split_at(1).1;
59
60 let mut command: Option<Cmd> = None;
61
62 let mut mod_options: Option<ModOptions> = None;
63 let mut mod_id: Option<IDSelector> = None;
64 let mut set_version = false;
65 let mut all_lists = false;
66 let mut clean = false;
67 let mut direct_download = true;
68 let mut delete_old = false;
69 let mut list: Option<List> = None;
70 let mut list_options: Option<ListOptions> = None;
71 let mut list_id: Option<String> = None;
72 let mut list_mcversion: Option<String> = None;
73 let mut modloader: Option<Modloader> = None;
74 let mut directory: Option<String> = None;
75 let mut io_options: Option<IoOptions> = None;
76 let mut file: Option<String> = None;
77
78 for arg in args {
79 let arg_split: Vec<&str> = arg.trim().split(' ').collect();
80 match arg_split[0] {
81 "v" | "version" => {
82 command = Some(Cmd::Version);
83 },
84 "d" | "download" => {
85 command = Some(Cmd::Download);
86 },
87 "u" | "update" => {
88 command = Some(Cmd::Update);
89 },
90 "ma" => {
91 command = Some(Cmd::Mod);
92 mod_options = Some(ModOptions::Add);
93 if arg_split.len() == 2 {
94 mod_id = Some(IDSelector::ModificationID(String::from(arg_split[1])));
95 } else {
96 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list mod slug or id"));
97 }
98 },
99 //TODO impl this
100 "mv" => {
101 command = Some(Cmd::Mod);
102 mod_options = Some(ModOptions::Add);
103 if arg_split.len() == 2 {
104 mod_id = Some(IDSelector::VersionID(String::from(arg_split[1])));
105 } else {
106 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a version id"));
107 };
108 },
109 "mr" => {
110 command = Some(Cmd::Mod);
111 mod_options = Some(ModOptions::Remove);
112 if arg_split.len() == 2 {
113 mod_id = Some(IDSelector::ModificationID(String::from(arg_split[1])));
114 } else {
115 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a mod id"));
116 };
117 },
118 "set_version" => {
119 set_version = true;
120 },
121 "all_lists" => {
122 all_lists = true;
123 },
124 "clean" => {
125 clean = true;
126 },
127 "no_download" => {
128 direct_download = false;
129 },
130 "delete_old" => {
131 delete_old = true;
132 },
133 "l" => {
134 if arg_split.len() == 2 {
135 list = Some(lists_get(config.clone(), String::from(arg_split[1]))?);
136 } else {
137 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a list via it's id"));
138 }
139 }
140 "la" => {
141 command = Some(Cmd::List);
142 list_options = Some(ListOptions::Add);
143 if arg_split.len() == 2 {
144 list_id = Some(String::from(arg_split[1]));
145 } else {
146 return Err(MLError::new(ErrorType::ArgumentError, "Please give the new list an id"));
147 }
148 },
149 "lr" => {
150 command = Some(Cmd::List);
151 list_options = Some(ListOptions::Remove);
152 },
153 "lc" => {
154 command = Some(Cmd::List);
155 list_options = Some(ListOptions::Change);
156 },
157 "lv" => {
158 command = Some(Cmd::List);
159 list_options = Some(ListOptions::Version);
160 if arg_split.len() == 2 {
161 list_mcversion = Some(String::from(arg_split[1]));
162 } else {
163 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
164 }
165 },
166 "mcv" => {
167 if arg_split.len() == 2 {
168 list_mcversion = Some(String::from(arg_split[1]));
169 } else {
170 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a minecraft version"));
171 }
172 },
173 "ml" => {
174 if arg_split.len() == 2 {
175 modloader = Some(Modloader::from(arg_split[1])?);
176 } else {
177 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a modloader"));
178 }
179 },
180 "dir" => {
181 if arg_split.len() == 2 {
182 directory = Some(String::from(arg_split[1]));
183 } else {
184 return Err(MLError::new(ErrorType::ArgumentError, "Please specify a directory"));
185 }
186 },
187 "export" => {
188 command = Some(Cmd::Io);
189 io_options = Some(IoOptions::Export);
190 },
191 "import" => {
192 command = Some(Cmd::Io);
193 io_options = Some(IoOptions::Import);
194 },
195 "f" => {
196 file = Some(String::from(arg_split[1]));
197 },
198 "setup" => {
199 command = Some(Cmd::Setup);
200 }
201 _ => return Err(MLError::new(ErrorType::ArgumentError, format!("Unknown Argument ({})", arg_split[0]).as_str())),
202 }
203 }
204
205 Ok(Self {
206 command,
207 mod_options,
208 mod_id,
209 set_version,
210 all_lists,
211 clean,
212 direct_download,
213 delete_old,
214 list,
215 list_options,
216 list_id,
217 list_mcversion,
218 modloader,
219 directory,
220 io_options,
221 file
222 })
223 }
224}
225
226pub async fn get_input(config: Cfg, args: Vec<String>) -> MLE<Input> {
227 let input = Input::from(config.clone(), args)?;
228
229 if input.command.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "No command specified")); };
230
231 match input.clone().command.unwrap() {
232 Cmd::Mod => check_mod(input, config),
233 Cmd::List => check_list(input, config).await,
234 _ => Ok(input),
235 }
236}
237
238fn check_mod(mut input: Input, config: Cfg) -> MLE<Input> {
239 if input.mod_options.is_none() {
240 return Err(MLError::new(ErrorType::ArgumentError, "No mod option"));
241 };
242 match input.clone().mod_options.unwrap() {
243 ModOptions::Add => {
244 if input.mod_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "No mod id/slug or version id")); };
245 if input.list_id.is_none() { input.list = Some(get_current_list(config)?); };
246 Ok(input)
247 },
248 ModOptions::Remove => {
249 if input.mod_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "MODS_NO_MODID")); };
250 if input.list_id.is_none() { input.list = Some(get_current_list(config)?); };
251 Ok(input)
252 },
253 }
254}
255
256async fn check_list(mut input: Input, config: Cfg) -> MLE<Input> {
257 if input.list_options.is_none() {
258 return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_ARGUMENT"));
259 };
260 match input.clone().list_options.unwrap() {
261 ListOptions::Add => {
262 if input.list_id.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "no list id specified")); };
263 if input.list_mcversion.is_none() {
264 println!("No Minecraft Version specified, defaulting to latest release");
265 input.list_mcversion = Some(get_minecraft_version(&config.apis.modrinth, MCVersionType::Release).await);
266 };
267 if input.directory.is_none() {
268 let id = input.clone().list_id.unwrap();
269 println!("No download directory specified, defaulting to ./downloads/{}", id);
270 input.directory = Some(format!("./downloads/{}", id))
271 };
272 if input.modloader.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "no modloader specified")); };
273 Ok(input)
274 },
275 ListOptions::Remove => {
276 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
277 Ok(input)
278 },
279 ListOptions::Change => {
280 if input.list.is_none() { return Err(MLError::new(ErrorType::ArgumentError, "NO_LIST_SPECIFIED")); };
281 Ok(input)
282 },
283 ListOptions::Version => {
284 if input.list.is_none() {
285 println!("No list specified, using default");
286 input.list = Some(get_current_list(config)?);
287 };
288 Ok(input)
289 }
290 }
291}
292
293#[test]
294fn input_from() {
295 let config = Cfg::init("modlist.toml").unwrap();
296 assert_eq!(
297 Input::from(config, vec![String::from("-la test -lv 1.19.3")]).unwrap(),
298 Input {
299 command: Some(Cmd::List),
300 mod_options: None,
301 mod_id: None,
302 set_version: false,
303 all_lists: false,
304 clean: false,
305 direct_download: false,
306 delete_old: false,
307 list: None,
308 list_options: Some(ListOptions::Add),
309 list_id: Some(String::from("test")),
310 list_mcversion: Some(String::from("1.19.3")),
311 modloader: None,
312 directory: None,
313 io_options: None,
314 file: None,
315 }
316 );
317
318}
319
320#[tokio::test]
321async fn get_input_test() {
322 let config = Cfg::init("modlist.toml").unwrap();
323 assert_eq!(
324 get_input(config.clone(), vec![String::from("-ma test")]).await.unwrap(),
325 Input {
326 command: Some(Cmd::Mod),
327 mod_options: Some(ModOptions::Add),
328 mod_id: Some(IDSelector::ModificationID(String::from("test"))),
329 set_version: false,
330 all_lists: false,
331 clean: false,
332 direct_download: false,
333 delete_old: false,
334 list: Some(lists_get(config.clone(), String::from("one")).unwrap()),
335 list_options: None,
336 list_id: None,
337 list_mcversion: None,
338 modloader: None,
339 directory: None,
340 io_options: None,
341 file: None
342 }
343 )
344}
diff --git a/src/lib.rs b/src/lib.rs
index eb845d8..43f0fe7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,36 +1,37 @@
1pub mod apis; 1pub mod apis;
2pub mod config;
3pub mod commands; 2pub mod commands;
4pub mod input; 3pub mod config;
5pub mod db; 4pub mod db;
6pub mod error; 5pub mod error;
7pub mod files; 6pub mod files;
8 7
9use std::path::Path; 8use std::{fmt::Display, path::Path};
10 9
11pub use apis::*; 10pub use apis::*;
12pub use commands::*; 11pub use commands::*;
13use error::{MLE, ErrorType, MLError}; 12use error::{ErrorType, MLError, MLE};
14 13
15#[derive(Debug, Clone, PartialEq, Eq)] 14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum Modloader { 15pub enum Modloader {
17 Fabric, 16 Fabric,
18 Forge 17 Forge,
19} 18}
20 19
21impl Modloader { 20impl Modloader {
22 fn to_string(&self) -> String { 21 pub fn from(string: &str) -> MLE<Modloader> {
23 match self {
24 Modloader::Fabric => String::from("fabric"),
25 Modloader::Forge => String::from("forge"),
26 }
27 }
28
29 fn from(string: &str) -> MLE<Modloader> {
30 match string { 22 match string {
31 "forge" => Ok(Modloader::Forge), 23 "forge" => Ok(Modloader::Forge),
32 "fabric" => Ok(Modloader::Fabric), 24 "fabric" => Ok(Modloader::Fabric),
33 _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")) 25 _ => Err(MLError::new(ErrorType::ArgumentError, "UNKNOWN_MODLOADER")),
26 }
27 }
28}
29
30impl Display for Modloader {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Modloader::Fabric => write!(f, "fabric"),
34 Modloader::Forge => write!(f, "forge"),
34 } 35 }
35 } 36 }
36} 37}
diff --git a/src/main.rs b/src/main.rs
index 32727c7..2006856 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,65 +1,269 @@
1use std::{env, process}; 1use clap::{Parser, Subcommand};
2use modlist::{
3 config::Cfg,
4 db::{config_get_current_list, lists_get, lists_get_all_ids},
5 devdir, download, export, get_current_list, import, list_add, list_change, list_remove,
6 list_version, mod_add, mod_remove, update, IDSelector, List, Modloader,
7};
2 8
3use modlist::{config::Cfg, input::{get_input, Cmd}, update, download, list, io, modification, setup}; 9//TODO implement remote sql db
10
11//TODO make default list optional
12#[derive(Parser)]
13#[command(author, version, about)]
14struct Cli {
15 #[command(subcommand)]
16 command: Commands,
17}
18
19#[derive(Subcommand)]
20enum Commands {
21 r#Mod {
22 #[command(subcommand)]
23 command: ModCommands,
24 },
25 List {
26 #[command(subcommand)]
27 command: ListCommands,
28 },
29 Download {
30 /// download all lists
31 #[arg(short, long)]
32 all: bool,
33
34 /// clean all mods before downloading them
35 #[arg(short, long)]
36 clean: bool,
37
38 /// remove disabled versions
39 #[arg(short, long)]
40 remove: bool,
41 },
42 Update {
43 /// download all lists
44 #[arg(short, long)]
45 all: bool,
46
47 /// directly download updated mods
48 #[arg(short, long)]
49 download: bool,
50
51 /// clean all mods before downloading them
52 #[arg(short, long)]
53 clean: bool,
54
55 /// delete disabled versions
56 #[arg(short, long)]
57 remove: bool,
58 },
59 Import {
60 #[arg(short, long)]
61 file: Option<String>,
62
63 /// directly download imported mods
64 #[arg(short, long)]
65 download: bool,
66 },
67 Export {
68 /// the list you want to export
69 list: Option<String>,
70 },
71}
72
73#[derive(Subcommand)]
74enum ModCommands {
75 Add {
76 /// id of the mod/version
77 id: String,
78
79 /// set id mode to version
80 #[arg(short, long)]
81 version: bool,
82
83 /// directly download the mod
84 #[arg(short, long)]
85 download: bool,
86
87 /// lock the version added
88 #[arg(/* short , */long)]
89 lock: bool,
90
91 /// optional List selection, else default list will be used
92 #[arg(short, long)]
93 list: Option<String>,
94 },
95 Remove {
96 /// id, name or title of the mod
97 id: String,
98
99 /// optional List selection, else default list will be used
100 #[arg(short, long)]
101 list: Option<String>,
102 },
103}
104
105#[derive(Subcommand)]
106enum ListCommands {
107 Add {
108 /// list id
109 id: String,
110
111 directory: String,
112
113 modloader: Option<String>,
114
115 version: Option<String>,
116 },
117 Remove {
118 /// id, name or title of the list
119 id: String,
120 },
121 List,
122 Change {
123 /// id of the list to change to
124 id: String,
125 },
126 Version {
127 /// list id
128 id: String,
129 /// desired minecraft version
130 version: String,
131
132 /// directly download updated mods
133 #[arg(long, short)]
134 download: bool,
135
136 /// delete disabled versions
137 #[arg(short, long)]
138 remove: bool,
139 },
140}
4 141
5#[tokio::main] 142#[tokio::main]
6async fn main() { 143async fn main() {
144 let cli = Cli::parse();
145
7 let config = Cfg::init("modlist.toml").unwrap(); 146 let config = Cfg::init("modlist.toml").unwrap();
8 147 println!("{:?}", config);
9 let mut args: Vec<String> = env::args().collect(); 148
10 args.reverse(); 149 //TODO setup? maybe setup on install
11 args.pop(); 150 match cli.command {
12 args.reverse(); 151 Commands::Mod { command } => {
13 152 match command {
14 if args.is_empty() { 153 #[allow(unused_variables)]
15 println!("Please enter an argument"); 154 ModCommands::Add {
16 process::exit(1); 155 id,
17 }; 156 version,
18 157 list,
19 let input = match get_input(config.clone(), args).await { 158 download,
20 Ok(i) => i, 159 lock,
21 Err(e) => { 160 } => {
22 println!("{}", e); 161 let listf = match list {
23 process::exit(1); 162 Some(list) => lists_get(config.clone(), list).unwrap(),
163 None => lists_get(
164 config.clone(),
165 config_get_current_list(config.clone()).unwrap(),
166 )
167 .unwrap(),
168 };
169
170 let marked_id = match version {
171 true => IDSelector::VersionID(id),
172 false => IDSelector::ModificationID(id),
173 };
174
175 mod_add(config, vec![marked_id], listf, download, lock).await
176 }
177 ModCommands::Remove { id, list } => {
178 //TODO add output
179 //TODO add success even if no file found
180 let listf = match list {
181 Some(list) => lists_get(config.clone(), list).unwrap(),
182 None => lists_get(
183 config.clone(),
184 config_get_current_list(config.clone()).unwrap(),
185 )
186 .unwrap(),
187 };
188 mod_remove(config, &id, listf)
189 }
190 }
24 } 191 }
25 }; 192 Commands::List { command } => {
26 193 match command {
27 match input.clone().command.unwrap() { 194 ListCommands::Add {
28 Cmd::Mod => { 195 id,
29 modification(config, input).await 196 directory,
30 }, 197 modloader,
31 Cmd::List => { 198 version,
32 list(config, input).await 199 } => {
33 }, 200 let ml = match modloader {
34 Cmd::Update => { 201 Some(ml) => Modloader::from(&ml).unwrap(),
35 update(config, input).await 202 //TODO add default modloader to config
36 }, 203 None => Modloader::Fabric,
37 Cmd::Download => { 204 };
38 download(config, input).await
39 },
40 Cmd::Io => {
41 io(config, input).await
42 },
43 Cmd::Version => {
44 show_version();
45 Ok(())
46 },
47 Cmd::Setup => {
48 setup(config).await
49 },
50 }.unwrap()
51}
52 205
53fn show_version() { 206 let ver = match version {
54 match std::env::var("DEV") { 207 Some(ver) => ver,
55 Ok(dev) => { 208 //TODO get latest version
56 let devint = dev.parse::<i32>().unwrap(); 209 //TODO impl config for specific version or latest or latest snap
57 if devint >= 1 { 210 None => "1.19.4".to_string(),
58 println!("Modlist by FxQnLr v{} (DEV)", env!("CARGO_PKG_VERSION")); 211 };
212
213 list_add(config, id, ver, ml, directory)
214 }
215 ListCommands::Remove { id } => list_remove(config, id),
216 ListCommands::List => {
217 todo!()
218 }
219 ListCommands::Change { id } => list_change(config, id),
220 ListCommands::Version {
221 id,
222 version,
223 download,
224 remove,
225 } => list_version(config, id, version, download, remove).await,
226 }
227 }
228 //TODO a add specific list
229 Commands::Update {
230 all,
231 download,
232 clean,
233 remove,
234 } => {
235 let mut liststack: Vec<List> = vec![];
236 if all {
237 let list_ids = lists_get_all_ids(config.clone()).unwrap();
238 for id in list_ids {
239 liststack.push(lists_get(config.clone(), id).unwrap());
240 }
59 } else { 241 } else {
60 println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")); 242 let current = get_current_list(config.clone()).unwrap();
243 println!("Update list {}:", current.id);
244 liststack.push(current)
61 } 245 }
62 }, 246 update(config, liststack, clean, download, remove).await
63 Err(..) => println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")), 247 }
248 //TODO add specific list
249 Commands::Download { all, clean, remove } => download(config, all, clean, remove).await,
250 Commands::Import { file, download } => {
251 let filestr: String = match file {
252 Some(args) => args,
253 None => devdir(
254 dirs::home_dir()
255 .unwrap()
256 .join("mlexport.toml")
257 .into_os_string()
258 .into_string()
259 .unwrap()
260 .as_str(),
261 ),
262 };
263
264 import(config, filestr, download).await
265 }
266 Commands::Export { list } => export(config, list),
64 } 267 }
268 .unwrap();
65} 269}