From 09e3c066b60e7a15b6f5a302e351ad9cb16c78d9 Mon Sep 17 00:00:00 2001 From: Warren Hood Date: Sat, 17 Aug 2024 17:11:04 +0200 Subject: [PATCH] Implemented initial WIP mod resolver --- Cargo.lock | 644 +++++++++++++++++++++----------------- Cargo.toml | 5 +- src/main.rs | 35 ++- src/mod_meta.rs | 4 +- src/modpack.rs | 11 +- src/providers/mod.rs | 22 ++ src/providers/modrinth.rs | 144 +++++++++ src/providers/raw.rs | 0 src/resolver.rs | 115 ++++--- 9 files changed, 632 insertions(+), 348 deletions(-) create mode 100644 src/providers/mod.rs create mode 100644 src/providers/modrinth.rs create mode 100644 src/providers/raw.rs diff --git a/Cargo.lock b/Cargo.lock index 866773a..bf2b246 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,30 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstream" version = "0.6.15" @@ -90,6 +66,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -117,18 +99,24 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.7.1" @@ -137,9 +125,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "shlex", ] @@ -150,21 +138,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.6", -] - [[package]] name = "clap" version = "4.5.15" @@ -211,12 +184,31 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -224,27 +216,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "ferinth" -version = "2.11.0" +name = "errno" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e02ddb7dbc6a683bf0e53786279a3a621977609df68eedb3311d05a8de807eb" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "chrono", - "lazy-regex", - "once_cell", - "reqwest", - "serde", - "serde_json", - "thiserror", - "url", + "libc", + "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -269,6 +276,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + [[package]] name = "futures-task" version = "0.3.30" @@ -304,6 +317,25 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -371,6 +403,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -396,7 +429,22 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -419,29 +467,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "idna" version = "0.5.0" @@ -489,29 +514,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy-regex" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576c8060ecfdf2e56995cf3274b4f2d71fa5e4fa3607c1c0b63c10180ee58741" -dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", -] - -[[package]] -name = "lazy-regex-proc_macros" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9efb9e65d4503df81c615dc33ff07042a9408ac7f26b45abee25566f7fbfd12c" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn", -] - [[package]] name = "lhash" version = "1.1.0" @@ -520,9 +522,25 @@ checksum = "744a4c881f502e98c2241d2e5f50040ac73b30194d64452bb6260393b53f0dc9" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] name = "log" @@ -535,8 +553,9 @@ name = "mcmpmgr" version = "0.1.0" dependencies = [ "clap", - "ferinth", "lhash", + "reqwest", + "semver", "serde", "tokio", "toml", @@ -576,12 +595,20 @@ dependencies = [ ] [[package]] -name = "num-traits" -version = "0.2.19" +name = "native-tls" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "autocfg", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -599,6 +626,73 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -638,13 +732,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "ppv-lite86" -version = "0.2.20" +name = "pkg-config" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro2" @@ -655,54 +746,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quinn" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" -dependencies = [ - "bytes", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" -dependencies = [ - "bytes", - "rand", - "ring", - "rustc-hash", - "rustls", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-udp" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" -dependencies = [ - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.52.0", -] - [[package]] name = "quote" version = "1.0.36" @@ -713,64 +756,14 @@ dependencies = [ ] [[package]] -name = "rand" -version = "0.8.5" +name = "redox_syscall" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "libc", - "rand_chacha", - "rand_core", + "bitflags 2.6.0", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - [[package]] name = "reqwest" version = "0.12.5" @@ -779,37 +772,38 @@ checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64", "bytes", + "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", "rustls-pemfile", - "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", "winreg", ] @@ -835,10 +829,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rustc-hash" -version = "2.0.0" +name = "rustix" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] [[package]] name = "rustls" @@ -847,7 +848,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -887,6 +887,53 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + [[package]] name = "serde" version = "1.0.207" @@ -909,9 +956,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", @@ -946,6 +993,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -1007,23 +1063,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] -name = "thiserror" -version = "1.0.63" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "thiserror-impl", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "thiserror-impl" -version = "1.0.63" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "proc-macro2", - "quote", - "syn", + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] @@ -1051,11 +1121,35 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.0" @@ -1067,6 +1161,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.19" @@ -1189,7 +1296,6 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] [[package]] @@ -1198,6 +1304,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "want" version = "0.3.1" @@ -1290,24 +1402,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -1326,6 +1420,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -1466,27 +1569,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index de5a917..682b737 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,9 @@ edition = "2021" [dependencies] clap = { version = "4.5.15", features = ["derive"] } -ferinth = "2.11.0" lhash = { version = "1.1.0", features = ["sha1", "sha512"] } +reqwest = { version = "0.12.5", features = ["json"] } +semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0.207", features = ["derive"] } -tokio = "1.39.2" +tokio = { version = "1.39.2", features = ["full"] } toml = "0.8.19" diff --git a/src/main.rs b/src/main.rs index cfa797f..dfb24fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,12 @@ -mod modpack; mod mod_meta; +mod modpack; +mod providers; mod resolver; use clap::{Parser, Subcommand}; use mod_meta::{ModMeta, ModProvider}; use modpack::ModpackMeta; -use std::{error::Error, path::PathBuf}; +use std::{borrow::BorrowMut, error::Error, path::PathBuf}; /// A Minecraft Modpack Manager #[derive(Parser)] @@ -61,7 +62,8 @@ enum Commands { }, } -fn main() -> Result<(), Box> { +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { let cli = Cli::parse(); if let Some(command) = cli.command { @@ -123,6 +125,7 @@ fn main() -> Result<(), Box> { url, } => { let mut modpack_meta = ModpackMeta::load_from_current_directory()?; + let old_modpack_meta = modpack_meta.clone(); let mut mod_meta = ModMeta::new(&name)?; if let Some(url) = url { @@ -134,9 +137,29 @@ fn main() -> Result<(), Box> { modpack_meta = modpack_meta.add_mod(&mod_meta); modpack_meta.save_current_dir_project()?; - let mut modpack_lock = resolver::PinnedPackMeta::load_from_current_directory()?; - modpack_lock.pin_mod(&mod_meta); - modpack_lock.save_current_dir_lock()?; + let revert_modpack_meta = |e| -> ! { + let revert_result = old_modpack_meta.save_current_dir_project(); + if let Err(result) = revert_result { + panic!("Failed to revert modpack meta: {}", result); + } + panic!("Reverted modpack meta:\n{}", e); + }; + + match resolver::PinnedPackMeta::load_from_current_directory().await { + Ok(mut modpack_lock) => { + let pin_result = modpack_lock.pin_mod(&mod_meta, &modpack_meta).await; + if let Err(e) = pin_result { + revert_modpack_meta(e); + } + + if let Err(e) = modpack_lock.save_current_dir_lock() { + revert_modpack_meta(e); + } + } + Err(e) => { + revert_modpack_meta(e); + } + }; } } }; diff --git a/src/mod_meta.rs b/src/mod_meta.rs index 2acd81d..621cc6c 100644 --- a/src/mod_meta.rs +++ b/src/mod_meta.rs @@ -2,7 +2,7 @@ use std::{borrow::BorrowMut, error::Error}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] pub enum ModProvider { /// Get mods from CurseForge CurseForge, @@ -29,7 +29,7 @@ impl std::str::FromStr for ModProvider { pub struct ModMeta { pub name: String, pub version: String, - providers: Option>, + pub providers: Option>, download_url: Option, } diff --git a/src/modpack.rs b/src/modpack.rs index a7ce4d3..55ce528 100644 --- a/src/modpack.rs +++ b/src/modpack.rs @@ -1,8 +1,5 @@ use std::{collections::HashMap, error::Error, path::PathBuf}; - -use ferinth::Ferinth; use serde::{Deserialize, Serialize}; - use crate::mod_meta::{ModMeta, ModProvider}; const MODPACK_FILENAME: &str = "modpack.toml"; @@ -35,13 +32,13 @@ impl std::str::FromStr for ModLoader { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct ModpackMeta { pack_name: String, - mc_version: String, - modloader: ModLoader, + pub mc_version: String, + pub modloader: ModLoader, mods: HashMap, - default_providers: Vec, + pub default_providers: Vec, } impl ModpackMeta { diff --git a/src/providers/mod.rs b/src/providers/mod.rs new file mode 100644 index 0000000..61d90e5 --- /dev/null +++ b/src/providers/mod.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; +use serde::{Deserialize, Serialize}; +use crate::mod_meta::ModMeta; + +pub mod modrinth; +pub mod raw; + +#[derive(Serialize, Deserialize)] +enum FileSource { + Download { url: String, sha1: String, sha512: String}, + Local { path: PathBuf, sha1: String, sha512: String }, +} + +#[derive(Serialize, Deserialize)] +pub struct PinnedMod { + /// Source of the files for the mod + source: Vec, + /// Version of mod + version: semver::Version, + /// Pinned dependencies of a pinned mod + deps: Option> +} \ No newline at end of file diff --git a/src/providers/modrinth.rs b/src/providers/modrinth.rs new file mode 100644 index 0000000..5e0b7b1 --- /dev/null +++ b/src/providers/modrinth.rs @@ -0,0 +1,144 @@ +use serde::{Deserialize, Serialize}; +use std::error::Error; + +use super::PinnedMod; +use crate::{mod_meta::ModMeta, modpack::ModpackMeta, providers::FileSource}; + +pub struct Modrinth { + client: reqwest::Client, +} + +#[derive(Serialize, Deserialize, Debug)] +struct VersionDeps { + dependency_type: String, + project_id: String, + file_name: Option, + version_id: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +struct VersionHashes { + sha1: String, + sha512: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct VersionFiles { + // file_type: String, + filename: String, + hashes: VersionHashes, + // primary: bool, + // size: i64, + url: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct ModrinthProjectVersion { + author_id: String, + date_published: String, + dependencies: Vec, + // downloads: i64, + files: Vec, + // loaders: Vec, + // name: String, + // project_id: String, + version_number: semver::Version, + // version_type: String, +} + +impl Modrinth { + pub fn new() -> Self { + Self { + ..Default::default() + } + } + + /// Resolve a list of mod candidates in order of newest to oldest + pub async fn resolve( + &self, + mod_meta: &ModMeta, + pack_meta: &ModpackMeta, + ) -> Result, Box> { + let mut versions = self.get_project_versions(&mod_meta.name, pack_meta).await?; + versions.sort_by_key(|v| v.version_number.clone()); + versions.reverse(); + + if mod_meta.version == "*" { + return Ok(versions + .into_iter() + .map(|v| PinnedMod { + source: v + .files + .into_iter() + .map(|f| FileSource::Download { + url: f.url, + sha1: f.hashes.sha1, + sha512: f.hashes.sha512, + }) + .collect(), + version: v.version_number, + deps: None, // TODO: Implement automagic transitive dep installation and pinning + }) + .collect()); + } + + // TODO: Implement more general version constraints + let expected_version = semver::Version::parse(&mod_meta.version)?; + for v in versions.into_iter() { + if v.version_number == expected_version { + return Ok(vec![PinnedMod { + source: v + .files + .into_iter() + .map(|f| FileSource::Download { + url: f.url, + sha1: f.hashes.sha1, + sha512: f.hashes.sha512, + }) + .collect(), + version: v.version_number, + deps: None, + }]); + } + } + + Err(format!( + "Couldn't find a version for mod '{}' that satisfies the version constraint `{}`", + mod_meta.name, mod_meta.version + ) + .into()) + } + + async fn get_project_versions( + &self, + mod_id: &str, + pack_meta: &ModpackMeta, + ) -> Result, Box> { + let project_Versions: Vec = self + .client + .get(format!( + "https://api.modrinth.com/v2/project/{mod_id}/version" + )) + .query(&[ + ( + "loaders", + format!("[\"{}\"]", pack_meta.modloader.to_string().to_lowercase()), + ), + ("game_versions", format!("[\"{}\"]", pack_meta.mc_version)), + ]) + .send() + .await? + .json() + .await?; + + Ok(project_Versions) + } +} + +impl Default for Modrinth { + fn default() -> Self { + Self { + client: Default::default(), + } + } +} diff --git a/src/providers/raw.rs b/src/providers/raw.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/resolver.rs b/src/resolver.rs index 152900d..5717815 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1,68 +1,81 @@ use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, error::Error, path::PathBuf}; +use std::{ + borrow::Borrow, + collections::{HashMap, HashSet}, + error::Error, + path::PathBuf, +}; -use crate::{mod_meta::ModMeta, modpack::ModpackMeta}; +use crate::{ + mod_meta::{ModMeta, ModProvider}, + modpack::ModpackMeta, + providers::{modrinth::Modrinth, PinnedMod}, +}; const MODPACK_LOCK_FILENAME: &str = "modpack.lock"; -#[derive(Serialize, Deserialize)] -enum FileSource { - Download { url: String }, - Local { path: PathBuf }, -} - -#[derive(Serialize, Deserialize)] -struct PinnedMod { - /// Source of the file - source: FileSource, - /// SHA1 Hash - sha1: String, - /// SHA512 Hash - sha512: String, - /// Version of mod - version: String, -} - -impl PinnedMod { - pub fn resolve_mod(mod_metadata: &ModMeta) -> Self { - // TODO: Actually implement this - Self { - source: FileSource::Download { - url: format!("https://fake.url/mods/{}", mod_metadata.name), - }, - sha1: "FakeSha1".into(), - sha512: "FakeSha512".into(), - version: if mod_metadata.version == "*" { - "1.0.0-fake-latest-version".into() - } else { - mod_metadata.version.clone() - }, - } - } -} - #[derive(Serialize, Deserialize)] pub struct PinnedPackMeta { mods: HashMap, + #[serde(skip_serializing, skip_deserializing)] + modrinth: Modrinth, } impl PinnedPackMeta { pub fn new() -> Self { Self { mods: Default::default(), + modrinth: Modrinth::new(), } } - pub fn pin_mod(&mut self, mod_metadata: &ModMeta) -> &mut Self { - let pinned_mod = PinnedMod::resolve_mod(mod_metadata); - self.mods.insert(mod_metadata.name.clone(), pinned_mod); - self + pub async fn pin_mod( + &mut self, + mod_metadata: &ModMeta, + pack_metadata: &ModpackMeta, + ) -> Result<(), Box> { + let mod_providers = if let Some(mod_providers) = &mod_metadata.providers { + mod_providers + } else { + &vec![] + }; + let mut checked_providers: HashSet = HashSet::new(); + for mod_provider in mod_providers + .iter() + .chain(pack_metadata.default_providers.iter()) + { + if checked_providers.contains(&mod_provider) { + // No need to repeat a check for a provider if we have already checked it + continue; + } + checked_providers.insert(mod_provider.clone()); + match mod_provider { + crate::mod_meta::ModProvider::CurseForge => unimplemented!(), + crate::mod_meta::ModProvider::Modrinth => { + let pinned_mods = self.modrinth.resolve(&mod_metadata, pack_metadata).await; + if let Ok(pinned_mods) = pinned_mods { + pinned_mods.into_iter().for_each(|m| { + self.mods.insert(mod_metadata.name.clone(), m); + }); + } else if let Err(e) = pinned_mods { + return Err(format!( + "Failed to resolve mod '{}' (provider={:#?}) with constraint {}: {}", + mod_metadata.name, mod_provider, mod_metadata.version, e + ) + .into()); + } + } + crate::mod_meta::ModProvider::Raw => unimplemented!(), + }; + } + Ok(()) } - pub fn init(&mut self, modpack_meta: &ModpackMeta) { - modpack_meta.iter_mods().for_each(|m| { - self.pin_mod(m); - }); + pub async fn init(&mut self, modpack_meta: &ModpackMeta) -> Result<(), Box> { + for mod_meta in modpack_meta.iter_mods() { + self.pin_mod(mod_meta, modpack_meta).await?; + } + Ok(()) } pub fn save_to_file(&self, path: &PathBuf) -> Result<(), Box> { @@ -81,18 +94,20 @@ impl PinnedPackMeta { Ok(()) } - pub fn load_from_directory(directory: &PathBuf) -> Result> { + pub async fn load_from_directory(directory: &PathBuf) -> Result> { let modpack_lock_file_path = directory.clone().join(PathBuf::from(MODPACK_LOCK_FILENAME)); if !modpack_lock_file_path.exists() { let mut new_modpack_lock = Self::new(); - new_modpack_lock.init(&ModpackMeta::load_from_directory(directory)?); + new_modpack_lock + .init(&ModpackMeta::load_from_directory(directory)?) + .await?; return Ok(new_modpack_lock); }; let modpack_lock_contents = std::fs::read_to_string(modpack_lock_file_path)?; Ok(toml::from_str(&modpack_lock_contents)?) } - pub fn load_from_current_directory() -> Result> { - Self::load_from_directory(&std::env::current_dir()?) + pub async fn load_from_current_directory() -> Result> { + Self::load_from_directory(&std::env::current_dir()?).await } }