From 5ce2df9f6ed3d4b44974cc5bb0939c6a688eccad Mon Sep 17 00:00:00 2001 From: Ilesh Thiada Date: Sun, 17 Nov 2024 23:27:45 +0530 Subject: [PATCH] Implement recursive, resolution-time dependencies --- Cargo.lock | 556 +++++++++++++++++++++-------- Cargo.toml | 9 +- src/download.rs | 9 +- src/main.rs | 15 +- src/subcommands/list.rs | 23 +- src/subcommands/modpack/switch.rs | 9 +- src/subcommands/modpack/upgrade.rs | 8 +- src/subcommands/profile/switch.rs | 9 +- src/subcommands/remove.rs | 9 +- src/subcommands/upgrade.rs | 185 +++++++--- tests/util.rs | 14 +- 11 files changed, 605 insertions(+), 241 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86b1acd..2f15ab8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -69,49 +69,49 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.90" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -351,9 +351,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "bzip2" @@ -378,9 +378,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -448,9 +448,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.33" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb" +checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01" dependencies = [ "clap", ] @@ -469,15 +469,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" @@ -507,7 +507,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -535,9 +535,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" dependencies = [ "libc", ] @@ -624,9 +624,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", @@ -766,9 +766,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "ferinth" @@ -782,7 +782,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -797,7 +797,6 @@ dependencies = [ "ferinth", "fs_extra", "furse", - "futures", "indicatif", "inquire", "libium", @@ -812,9 +811,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -853,7 +852,7 @@ dependencies = [ "reqwest", "serde", "serde_repr", - "thiserror", + "thiserror 1.0.69", "url", ] @@ -907,9 +906,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand", "futures-core", @@ -1008,9 +1007,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" @@ -1135,9 +1134,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper", "hyper-util", @@ -1148,9 +1147,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1188,14 +1187,143 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1210,15 +1338,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -1244,16 +1372,7 @@ dependencies = [ "newline-converter", "once_cell", "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", + "unicode-width 0.1.14", ] [[package]] @@ -1348,14 +1467,13 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libium" version = "1.32.0" -source = "git+https://github.com/gorilla-devs/libium?rev=b7d2cd767b840313bc71dca643e4252c4e16dd3e#b7d2cd767b840313bc71dca643e4252c4e16dd3e" dependencies = [ "clap", "derive_more", @@ -1369,7 +1487,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "thiserror", + "thiserror 2.0.3", "url", "zip", "zip-extensions", @@ -1381,6 +1499,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -1584,9 +1708,9 @@ dependencies = [ [[package]] name = "octocrab" -version = "0.41.2" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2dfd11f6efbd39491d71a3864496f0b6f45e2d01b73b26c55d631c4e0dafaef" +checksum = "5235d5839910001bef2c3df99a88688c7c781e5b1fd5fe40c5d8fa8bd786ac5a" dependencies = [ "arc-swap", "async-trait", @@ -1619,6 +1743,7 @@ dependencies = [ "tower-http", "tracing", "url", + "web-time", ] [[package]] @@ -1700,18 +1825,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -1720,9 +1845,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1749,9 +1874,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", @@ -1800,18 +1925,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -1820,34 +1945,38 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash", "rustls", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" dependencies = [ + "cfg_aliases", "libc", "once_cell", "socket2", @@ -1911,9 +2040,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -1923,9 +2052,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1940,9 +2069,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -2032,9 +2161,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -2045,9 +2174,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.15" +version = "0.23.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" +checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" dependencies = [ "log", "once_cell", @@ -2085,6 +2214,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -2142,9 +2274,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -2152,18 +2284,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -2172,9 +2304,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -2276,7 +2408,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -2338,6 +2470,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2358,9 +2496,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2382,11 +2520,22 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -2397,18 +2546,38 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -2456,6 +2625,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -2473,9 +2652,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -2644,27 +2823,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -2677,6 +2841,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -2691,9 +2861,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" dependencies = [ "form_urlencoded", "idna", @@ -2707,6 +2877,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2811,6 +2993,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.6" @@ -3038,6 +3231,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xdg-home" version = "1.3.0" @@ -3048,6 +3253,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zbus" version = "4.4.0" @@ -3126,6 +3355,27 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -3146,6 +3396,28 @@ dependencies = [ "syn", ] +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zip" version = "2.2.0" @@ -3168,7 +3440,7 @@ dependencies = [ "pbkdf2", "rand", "sha1", - "thiserror", + "thiserror 1.0.69", "time", "zeroize", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 97d3963..7615f25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ rfd = { version = "0.14", optional = true, default-features = false, features = reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", ] } -tokio = { version = "1.40", default-features = false, features = [ +tokio = { version = "1.41", default-features = false, features = [ "rt-multi-thread", "macros", ] } @@ -54,14 +54,13 @@ clap = { version = "4.5", features = ["derive"] } clap_complete = "4.5" serde_json = "1.0" indicatif = "0.17" -octocrab = "0.41" +octocrab = "0.42" fs_extra = "1.3" ferinth = "2.11" colored = "2.1" -futures = "0.3" inquire = "0.7" -libium = { git = "https://github.com/gorilla-devs/libium", rev = "b7d2cd767b840313bc71dca643e4252c4e16dd3e" } -# libium = { path = "../libium" } +# libium = { git = "https://github.com/gorilla-devs/libium", rev = "b7d2cd767b840313bc71dca643e4252c4e16dd3e" } +libium = { path = "../libium" } # libium = "1.32" anyhow = "1.0" furse = "1.5" diff --git a/src/download.rs b/src/download.rs index 6904f08..a83145e 100644 --- a/src/download.rs +++ b/src/download.rs @@ -7,7 +7,6 @@ use fs_extra::{ dir::{copy as copy_dir, CopyOptions as DirCopyOptions}, file::{move_file, CopyOptions as FileCopyOptions}, }; -use futures::{stream::FuturesUnordered, StreamExt as _}; use indicatif::ProgressBar; use libium::{iter_ext::IterExt as _, upgrade::DownloadData}; use std::{ @@ -17,7 +16,7 @@ use std::{ sync::{Arc, Mutex}, time::Duration, }; -use tokio::sync::Semaphore; +use tokio::{sync::Semaphore, task::JoinSet}; /// Check the given `directory` /// @@ -112,7 +111,7 @@ pub async fn download( .lock() .expect("Mutex poisoned") .enable_steady_tick(Duration::from_millis(100)); - let mut tasks = FuturesUnordered::new(); + let mut tasks = JoinSet::new(); let semaphore = Arc::new(Semaphore::new( *PARALLEL_NETWORK.get_or_init(|| DEFAULT_PARALLEL_NETWORK), )); @@ -124,7 +123,7 @@ pub async fn download( let client = client.clone(); let output_dir = output_dir.clone(); - tasks.push(async move { + tasks.spawn(async move { let _permit = semaphore.acquire_owned().await?; let (length, filename) = downloadable @@ -150,7 +149,7 @@ pub async fn download( Ok::<(), Error>(()) }); } - while let Some(res) = tasks.next().await { + for res in tasks.join_all().await { res?; } Arc::try_unwrap(progress_bar) diff --git a/src/main.rs b/src/main.rs index 27098ac..c468150 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,7 @@ const CROSS: &str = "×"; static TICK: LazyLock = LazyLock::new(|| "✓".green()); pub static PARALLEL_NETWORK: OnceLock = OnceLock::new(); +// Bump up the default to ~50 pub const DEFAULT_PARALLEL_NETWORK: usize = 10; /// Indicatif themes @@ -184,7 +185,9 @@ async fn actual_main(mut cli_app: Ferium) -> Result<()> { (filename, None, None) => { println!("{} {}", "Unknown file:".yellow(), filename.dimmed()); } - (_, Some(mr_id), None) => send_ids.push(ModIdentifier::ModrinthProject(mr_id)), + (_, Some(mr_id), None) => { + send_ids.push(ModIdentifier::ModrinthProject(mr_id)); + } (_, None, Some(cf_id)) => { send_ids.push(ModIdentifier::CurseForgeProject(cf_id)); } @@ -265,13 +268,15 @@ async fn actual_main(mut cli_app: Ferium) -> Result<()> { format!("{} {:8}", "CF".red(), id.to_string().dimmed()), ModIdentifier::ModrinthProject(id) => format!("{} {:8}", "MR".green(), id.dimmed()), - ModIdentifier::GitHubRepository(_) => "GH".purple().to_string(), + ModIdentifier::GitHubRepository(..) => "GH".purple().to_string(), + _ => todo!(), }, match &mod_.identifier { ModIdentifier::ModrinthProject(_) | ModIdentifier::CurseForgeProject(_) => mod_.name.bold().to_string(), - ModIdentifier::GitHubRepository(id) => - format!("{}/{}", id.0.dimmed(), id.1.bold()), + ModIdentifier::GitHubRepository(owner, repo) => + format!("{}/{}", owner.dimmed(), repo.bold()), + _ => todo!(), }, ); } @@ -458,7 +463,7 @@ fn get_active_profile(config: &mut Config) -> Result<&mut Profile> { bail!("There are no profiles configured, add a profile using `ferium profile create`") } 1 => config.active_profile = 0, - n if n <= config.active_profile => { + n if config.active_profile >= n => { println!( "{}", "Active profile specified incorrectly, please pick a profile to use" diff --git a/src/subcommands/list.rs b/src/subcommands/list.rs index e2d4ce1..e1c90f6 100644 --- a/src/subcommands/list.rs +++ b/src/subcommands/list.rs @@ -3,13 +3,13 @@ use anyhow::{Context as _, Result}; use colored::Colorize as _; use ferinth::structures::{project::Project, user::TeamMember}; use furse::structures::mod_structs::Mod; -use futures::{stream::FuturesUnordered, StreamExt as _}; use libium::{ config::structs::{ModIdentifier, Profile}, iter_ext::IterExt as _, CURSEFORGE_API, GITHUB_API, MODRINTH_API, }; use octocrab::models::{repos::Release, Repository}; +use tokio::task::JoinSet; enum Metadata { CF(Mod), @@ -31,7 +31,7 @@ impl Metadata { Metadata::CF(p) => ModIdentifier::CurseForgeProject(p.id), Metadata::MD(p, _) => ModIdentifier::ModrinthProject(p.id.clone()), Metadata::GH(p, _) => { - ModIdentifier::GitHubRepository((p.owner.clone().unwrap().login, p.name.clone())) + ModIdentifier::GitHubRepository(p.owner.clone().unwrap().login, p.name.clone()) } } } @@ -42,26 +42,23 @@ pub async fn verbose(profile: &mut Profile, markdown: bool) -> Result<()> { eprint!("Querying metadata... "); } - let mut tasks = FuturesUnordered::new(); + let mut tasks = JoinSet::new(); let mut mr_ids = Vec::new(); let mut cf_ids = Vec::new(); for mod_ in &profile.mods { match mod_.identifier.clone() { ModIdentifier::CurseForgeProject(project_id) => cf_ids.push(project_id), ModIdentifier::ModrinthProject(project_id) => mr_ids.push(project_id), - ModIdentifier::GitHubRepository((owner, repo)) => { - tasks.push(async { + ModIdentifier::GitHubRepository(owner, repo) => { + let repo = GITHUB_API.repos(owner, repo); + tasks.spawn(async move { Ok::<_, anyhow::Error>(( - GITHUB_API.repos(&owner, &repo).get().await?, - GITHUB_API - .repos(owner, repo) - .releases() - .list() - .send() - .await?, + repo.get().await?, + repo.releases().list().send().await?, )) }); } + _ => todo!(), } } @@ -93,7 +90,7 @@ pub async fn verbose(profile: &mut Profile, markdown: bool) -> Result<()> { for project in cf_projects { metadata.push(Metadata::CF(project)); } - while let Some(res) = tasks.next().await { + for res in tasks.join_all().await { let (repo, releases) = res?; metadata.push(Metadata::GH(repo, releases.items)); } diff --git a/src/subcommands/modpack/switch.rs b/src/subcommands/modpack/switch.rs index 2a98900..849d3b6 100644 --- a/src/subcommands/modpack/switch.rs +++ b/src/subcommands/modpack/switch.rs @@ -40,10 +40,11 @@ pub fn switch(config: &mut Config, modpack_name: Option) -> Result<()> { }) .collect_vec(); - if let Ok(selection) = Select::new("Select which modpack to switch to", modpack_info) - .with_starting_cursor(config.active_modpack) - .raw_prompt() - { + let mut select = Select::new("Select which modpack to switch to", modpack_info); + if config.active_modpack < config.modpacks.len() { + select.starting_cursor = config.active_modpack; + } + if let Ok(selection) = select.raw_prompt() { config.active_modpack = selection.index; } Ok(()) diff --git a/src/subcommands/modpack/upgrade.rs b/src/subcommands/modpack/upgrade.rs index eaf0fd0..a7b617d 100644 --- a/src/subcommands/modpack/upgrade.rs +++ b/src/subcommands/modpack/upgrade.rs @@ -4,7 +4,6 @@ use crate::{ }; use anyhow::{Context as _, Result}; use colored::Colorize as _; -use futures::{stream::FuturesUnordered, StreamExt as _}; use indicatif::ProgressBar; use libium::{ config::structs::{Modpack, ModpackIdentifier}, @@ -22,6 +21,7 @@ use std::{ path::{Path, PathBuf}, time::Duration, }; +use tokio::task::JoinSet; pub async fn upgrade(modpack: &'_ Modpack) -> Result<()> { let mut to_download: Vec = Vec::new(); @@ -58,7 +58,7 @@ pub async fn upgrade(modpack: &'_ Modpack) -> Result<()> { let files = CURSEFORGE_API.get_files(file_ids).await?; println!("{} Fetched {} mods", &*TICK, files.len()); - let mut tasks = FuturesUnordered::new(); + let mut tasks = JoinSet::new(); let mut msg_shown = false; for file in files { match try_from_cf_file(file) { @@ -81,7 +81,7 @@ pub async fn upgrade(modpack: &'_ Modpack) -> Result<()> { println!("\n{}", "The following mod(s) have denied 3rd parties such as Ferium from downloading it".red().bold()); } msg_shown = true; - tasks.push(async move { + tasks.spawn(async move { let project = CURSEFORGE_API.get_mod(mod_id).await?; eprintln!( "- {} @@ -97,7 +97,7 @@ pub async fn upgrade(modpack: &'_ Modpack) -> Result<()> { } } - while let Some(res) = tasks.next().await { + for res in tasks.join_all().await { res?; } diff --git a/src/subcommands/profile/switch.rs b/src/subcommands/profile/switch.rs index fb01e18..ab2368d 100644 --- a/src/subcommands/profile/switch.rs +++ b/src/subcommands/profile/switch.rs @@ -44,10 +44,11 @@ pub fn switch(config: &mut Config, profile_name: Option) -> Result<()> { }) .collect_vec(); - if let Ok(selection) = Select::new("Select which profile to switch to", profile_info) - .with_starting_cursor(config.active_profile) - .raw_prompt() - { + let mut select = Select::new("Select which profile to switch to", profile_info); + if config.active_profile < config.profiles.len() { + select.starting_cursor = config.active_profile; + } + if let Ok(selection) = select.raw_prompt() { config.active_profile = selection.index; } Ok(()) diff --git a/src/subcommands/remove.rs b/src/subcommands/remove.rs index 39028bb..ca17770 100644 --- a/src/subcommands/remove.rs +++ b/src/subcommands/remove.rs @@ -20,12 +20,14 @@ pub fn remove(profile: &mut Profile, to_remove: Vec) -> Result<()> { match &mod_.identifier { ModIdentifier::CurseForgeProject(id) => format!("CF {:8}", id.to_string()), ModIdentifier::ModrinthProject(id) => format!("MR {id:8}"), - ModIdentifier::GitHubRepository(_) => "GH".to_string(), + ModIdentifier::GitHubRepository(..) => "GH".to_string(), + _ => todo!(), }, match &mod_.identifier { ModIdentifier::ModrinthProject(_) | ModIdentifier::CurseForgeProject(_) => mod_.name.clone(), - ModIdentifier::GitHubRepository(id) => format!("{}/{}", id.0, id.1), + ModIdentifier::GitHubRepository(owner, repo) => format!("{owner}/{repo}"), + _ => todo!(), }, ) }) @@ -44,9 +46,10 @@ pub fn remove(profile: &mut Profile, to_remove: Vec) -> Result<()> { || match &mod_.identifier { ModIdentifier::CurseForgeProject(id) => id.to_string() == to_remove, ModIdentifier::ModrinthProject(id) => id == &to_remove, - ModIdentifier::GitHubRepository((owner, name)) => { + ModIdentifier::GitHubRepository(owner, name) => { format!("{owner}/{name}").eq_ignore_ascii_case(&to_remove) } + _ => todo!(), } }) { items_to_remove.push(index); diff --git a/src/subcommands/upgrade.rs b/src/subcommands/upgrade.rs index 8bf4b34..0d4842e 100644 --- a/src/subcommands/upgrade.rs +++ b/src/subcommands/upgrade.rs @@ -6,21 +6,21 @@ use crate::{ }; use anyhow::{anyhow, bail, Result}; use colored::Colorize as _; -use futures::{stream::FuturesUnordered, StreamExt as _}; use indicatif::ProgressBar; use libium::{ config::{ filters::ProfileParameters as _, - structs::{ModLoader, Profile}, + structs::{Mod, ModIdentifier, ModLoader, Profile}, }, upgrade::{mod_downloadable, DownloadData}, }; use std::{ fs::read_dir, - sync::{Arc, Mutex}, + mem::take, + sync::{mpsc, Arc, Mutex}, time::Duration, }; -use tokio::sync::Semaphore; +use tokio::{sync::Semaphore, task::JoinSet}; /// Get the latest compatible downloadable for the mods in `profile` /// @@ -28,10 +28,14 @@ use tokio::sync::Semaphore; /// resolution will continue and the error return flag is set to true. pub async fn get_platform_downloadables(profile: &Profile) -> Result<(Vec, bool)> { let to_download = Arc::new(Mutex::new(Vec::new())); - let progress_bar = Arc::new(Mutex::new( - ProgressBar::new(profile.mods.len() as u64).with_style(STYLE_NO.clone()), - )); - let mut tasks = FuturesUnordered::new(); + let progress_bar = Arc::new(Mutex::new(ProgressBar::new(0).with_style(STYLE_NO.clone()))); + let mut tasks = JoinSet::new(); + let mut done_mods = Vec::new(); + let (mod_sender, mod_rcvr) = mpsc::channel(); + + // Wrap it again in an Arc so that I can count the references to it, + // because I cannot drop the main thread's sender due to the recursion + let mod_sender = Arc::new(mod_sender); println!("{}\n", "Determining the Latest Compatible Versions".bold()); let semaphore = Arc::new(Semaphore::new( @@ -48,54 +52,139 @@ pub async fn get_platform_downloadables(profile: &Profile) -> Result<(Vec { - progress_bar.println(format!( - "{} {:pad_len$} {}", - TICK.clone(), - mod_.name, - download_file.filename().dimmed() - )); - to_download + mod_sender.send(mod_)?; + } + + let mut initial = true; + + // A race condition exists where if the last task drops its sender before this thread receives the message, + // that particular message will get ignored. I used the ostrich algorithm to solve this. + + // `initial` accounts for the edge case where at first, + // no tasks have been spawned yet but there are messages in the channel + while Arc::strong_count(&mod_sender) > 1 || initial { + if let Ok(mod_) = mod_rcvr.try_recv() { + initial = false; + + if done_mods.contains(&mod_.identifier) { + continue; + } + + // TODO: handle case where non-pinned version is requested for a pinned mod + + // Modrinth mods may request a different version of a mod that's already been processed + if let ModIdentifier::PinnedModrinthProject(proj_id, file_id) = &mod_.identifier { + if let Some(clash) = done_mods.iter().find(|m| { + matches!(m, ModIdentifier::PinnedModrinthProject(p, _) + | ModIdentifier::ModrinthProject(p) + if p == proj_id) + }) { + progress_bar .lock() .expect("Mutex poisoned") - .push(download_file); - Ok(true) - } - Err(err) => { - if let mod_downloadable::Error::ModrinthError( - ferinth::Error::RateLimitExceeded(_), - ) = err - { - // Immediately fail if the rate limit has been exceeded - progress_bar.finish_and_clear(); - bail!(err); - } - progress_bar.println(format!( - "{}", - format!("{CROSS} {:pad_len$} {err}", mod_.name).red() + .println(format!( + "{} Multiple versions of {} were requested, {} and {}. Ignoring the latter.", + "Warning:".bold().yellow(), + + match clash { + ModIdentifier::ModrinthProject(p) + | ModIdentifier::PinnedModrinthProject(p, _) => p, + _ => unreachable!(), + }, + match clash { + ModIdentifier::PinnedModrinthProject(_, f) => f, + ModIdentifier::ModrinthProject(_) => "latest", + _ => unreachable!(), + }, + file_id, )); - Ok(false) + continue; } } - }); - } - let mut error = false; - while let Some(res) = tasks.next().await { - let res = res?; - error |= !res; + done_mods.push(mod_.identifier.clone()); + progress_bar.lock().expect("Mutex poisoned").inc_length(1); + + let filters = profile.filters.clone(); + let dep_sender = Arc::clone(&mod_sender); + let semaphore = Arc::clone(&semaphore); + let to_download = Arc::clone(&to_download); + let progress_bar = Arc::clone(&progress_bar); + + tasks.spawn(async move { + let _permit = semaphore.acquire_owned().await?; + + let result = mod_.fetch_download_file(filters).await; + + progress_bar.lock().expect("Mutex poisoned").inc(1); + match result { + Ok(mut download_file) => { + progress_bar + .lock() + .expect("Mutex poisoned") + .println(format!( + "{} {:pad_len$} {}", + TICK.clone(), + mod_.name, + download_file.filename().dimmed() + )); + for dep in take(&mut download_file.dependencies) { + dep_sender.send(Mod { + name: format!( + "Dependency: {}", + match &dep { + ModIdentifier::CurseForgeProject(id) => id.to_string(), + ModIdentifier::ModrinthProject(id) + | ModIdentifier::PinnedModrinthProject(_, id) => + id.to_owned(), + _ => unreachable!(), + } + ), + identifier: dep, + filters: vec![], + override_filters: false, + })?; + } + to_download + .lock() + .expect("Mutex poisoned") + .push(download_file); + Ok(true) + } + Err(err) => { + if let mod_downloadable::Error::ModrinthError( + ferinth::Error::RateLimitExceeded(_), + ) = err + { + // Immediately fail if the rate limit has been exceeded + progress_bar + .lock() + .expect("Mutex poisoned") + .finish_and_clear(); + bail!(err); + } + progress_bar + .lock() + .expect("Mutex poisoned") + .println(format!( + "{}", + format!("{CROSS} {:pad_len$} {err}", mod_.name).red() + )); + Ok(false) + } + } + }); + } } + + let error = tasks + .join_all() + .await + .iter() + .any(|r| matches!(r, Ok(false))); + Arc::try_unwrap(progress_bar) .map_err(|_| anyhow!("Failed to run threads to completion"))? .into_inner()? diff --git a/tests/util.rs b/tests/util.rs index 36ef969..59165ce 100644 --- a/tests/util.rs +++ b/tests/util.rs @@ -1,11 +1,10 @@ use std::{ fs::{copy, create_dir}, - io::Result, + io::{Error, ErrorKind, Result}, process::Command, }; pub fn run_command(args: Vec<&str>, config_file: Option<&str>) -> Result<()> { - let mut args = args; let running = format!("./tests/configs/running/{}.json", rand::random::()); if let Some(config_file) = config_file { let _ = create_dir("./tests/configs/running"); @@ -14,17 +13,16 @@ pub fn run_command(args: Vec<&str>, config_file: Option<&str>) -> Result<()> { } let mut command = Command::new(env!("CARGO_BIN_EXE_ferium")); - let mut arguments = Vec::new(); - arguments.push("--config-file"); - arguments.push(&running); - arguments.append(&mut args); + let mut arguments = vec!["--config-file", &running]; + arguments.extend(args); command.args(arguments); let output = command.output()?; + if output.status.success() { Ok(()) } else { - Err(std::io::Error::new( - std::io::ErrorKind::Other, + Err(Error::new( + ErrorKind::Other, format!( "Command returned with exit code {:?}, stdout:{}, stderr:{}", output.status.code(),