Initial commit of mod_manager project
This commit is contained in:
parent
a84b2e802d
commit
6ef66541b1
459
Cargo.lock
generated
Normal file
459
Cargo.lock
generated
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell_polyfill",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"jiff",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jiff"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35"
|
||||||
|
dependencies = [
|
||||||
|
"jiff-static",
|
||||||
|
"log",
|
||||||
|
"portable-atomic",
|
||||||
|
"portable-atomic-util",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jiff-static"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.177"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md5"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mod_manager"
|
||||||
|
version = "0.1.1"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"md5",
|
||||||
|
"rayon",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic-util"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.145"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.110"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "mod_manager"
|
||||||
|
version = "0.1.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
sha2 = "0.10"
|
||||||
|
sha1 = "0.10"
|
||||||
|
md5 = "0.7"
|
||||||
|
rayon = "1.10"
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11"
|
||||||
364
src/main.rs
Normal file
364
src/main.rs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
use log::{error, info, warn};
|
||||||
|
use md5;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha1::Sha1;
|
||||||
|
use sha2::{Digest, Sha256, Sha512};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::{self, BufReader, BufWriter, Read};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
const MODS_DIR: &str = "mods";
|
||||||
|
const JSON_FILE: &str = "mods-list.json";
|
||||||
|
const BUILD_CLIENT_DIR: &str = "build_client";
|
||||||
|
const BUILD_SERVER_DIR: &str = "build_server";
|
||||||
|
const CATEGORIES: [&str; 3] = ["client-only", "server-only", "universal"];
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
struct ModEntry {
|
||||||
|
name: String,
|
||||||
|
sha512: String,
|
||||||
|
sha256: String,
|
||||||
|
sha1: String,
|
||||||
|
md5: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct ModList {
|
||||||
|
#[serde(rename = "client-only")]
|
||||||
|
client_only: Vec<ModEntry>,
|
||||||
|
#[serde(rename = "server-only")]
|
||||||
|
server_only: Vec<ModEntry>,
|
||||||
|
#[serde(rename = "universal")]
|
||||||
|
universal: Vec<ModEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Hashes {
|
||||||
|
sha512: String,
|
||||||
|
sha256: String,
|
||||||
|
sha1: String,
|
||||||
|
md5: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
let mods_dir = PathBuf::from(MODS_DIR);
|
||||||
|
let build_client_dir = PathBuf::from(BUILD_CLIENT_DIR);
|
||||||
|
let build_server_dir = PathBuf::from(BUILD_SERVER_DIR);
|
||||||
|
|
||||||
|
info!("Запуск менеджера модов");
|
||||||
|
println!("Выберите действие:");
|
||||||
|
println!("[1] Обновить моды");
|
||||||
|
println!("[2] Создать сборки");
|
||||||
|
println!("[3] Перевыбрать категории");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).expect("Ошибка ввода");
|
||||||
|
let choice = input.trim();
|
||||||
|
|
||||||
|
match choice {
|
||||||
|
"1" => update_mods(&mods_dir),
|
||||||
|
"2" => {
|
||||||
|
let data = load_json();
|
||||||
|
build(
|
||||||
|
&data.client_only,
|
||||||
|
&data.universal,
|
||||||
|
&mods_dir,
|
||||||
|
&build_client_dir,
|
||||||
|
"Client",
|
||||||
|
);
|
||||||
|
build(
|
||||||
|
&data.server_only,
|
||||||
|
&data.universal,
|
||||||
|
&mods_dir,
|
||||||
|
&build_server_dir,
|
||||||
|
"Server",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"3" => reassign_categories(&mods_dir),
|
||||||
|
_ => error!("Некорректный выбор"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_json() -> ModList {
|
||||||
|
match File::open(JSON_FILE) {
|
||||||
|
Ok(file) => serde_json::from_reader(BufReader::new(file)).unwrap_or_else(|e| {
|
||||||
|
warn!("Ошибка чтения JSON: {}. Создаём пустой.", e);
|
||||||
|
ModList {
|
||||||
|
client_only: vec![],
|
||||||
|
server_only: vec![],
|
||||||
|
universal: vec![],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Err(_) => ModList {
|
||||||
|
client_only: vec![],
|
||||||
|
server_only: vec![],
|
||||||
|
universal: vec![],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_json(data: &ModList) -> io::Result<()> {
|
||||||
|
let file = File::create(JSON_FILE)?;
|
||||||
|
serde_json::to_writer_pretty(BufWriter::new(file), data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_hashes_for_file(path: &PathBuf) -> Hashes {
|
||||||
|
let file = File::open(path).expect("Не удалось открыть файл");
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut sha512 = Sha512::new();
|
||||||
|
let mut sha256 = Sha256::new();
|
||||||
|
let mut sha1 = Sha1::new();
|
||||||
|
let mut md5_ctx = md5::Context::new();
|
||||||
|
let mut buffer = [0u8; 8192];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read(&mut buffer) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(n) => {
|
||||||
|
sha512.update(&buffer[..n]);
|
||||||
|
sha256.update(&buffer[..n]);
|
||||||
|
sha1.update(&buffer[..n]);
|
||||||
|
md5_ctx.consume(&buffer[..n]);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Ошибка чтения файла {}: {}", path.display(), e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Hashes {
|
||||||
|
sha512: format!("{:x}", sha512.finalize()),
|
||||||
|
sha256: format!("{:x}", sha256.finalize()),
|
||||||
|
sha1: format!("{:x}", sha1.finalize()),
|
||||||
|
md5: format!("{:x}", md5_ctx.compute()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_hashes(paths: &[PathBuf]) -> Vec<(PathBuf, Hashes)> {
|
||||||
|
paths
|
||||||
|
.par_iter()
|
||||||
|
.map(|p| (p.clone(), compute_hashes_for_file(p)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ask_category(mod_name: &str) -> &'static str {
|
||||||
|
println!("\nМод: {}", mod_name);
|
||||||
|
println!("Выберите категорию:");
|
||||||
|
for (i, cat) in CATEGORIES.iter().enumerate() {
|
||||||
|
println!("[{}] {}", i + 1, cat);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let mut input = String::new();
|
||||||
|
if io::stdin().read_line(&mut input).is_ok() {
|
||||||
|
if let Ok(n) = input.trim().parse::<usize>() {
|
||||||
|
if n > 0 && n <= CATEGORIES.len() {
|
||||||
|
return CATEGORIES[n - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Некорректный ввод, попробуйте снова:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_jar_files(dir: &PathBuf) -> Vec<PathBuf> {
|
||||||
|
match fs::read_dir(dir) {
|
||||||
|
Ok(entries) => entries
|
||||||
|
.filter_map(|e| e.ok().map(|e| e.path()))
|
||||||
|
.filter(|p| p.extension().map(|e| e == "jar").unwrap_or(false))
|
||||||
|
.collect(),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Не удалось прочитать папку {}: {}", MODS_DIR, e);
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_new_mods(
|
||||||
|
paths: &[PathBuf],
|
||||||
|
existing_sha512: &HashSet<String>,
|
||||||
|
) -> Vec<(ModEntry, &'static str)> {
|
||||||
|
let mut new_entries = vec![];
|
||||||
|
let file_hashes = compute_hashes(paths);
|
||||||
|
for (path, hashes) in file_hashes {
|
||||||
|
if existing_sha512.contains(&hashes.sha512) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let name = path
|
||||||
|
.file_name()
|
||||||
|
.expect("Нет имени файла")
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
info!("Новый мод: {}", name);
|
||||||
|
let cat = ask_category(&name);
|
||||||
|
let entry = ModEntry {
|
||||||
|
name,
|
||||||
|
sha512: hashes.sha512,
|
||||||
|
sha256: hashes.sha256,
|
||||||
|
sha1: hashes.sha1,
|
||||||
|
md5: hashes.md5,
|
||||||
|
};
|
||||||
|
new_entries.push((entry, cat));
|
||||||
|
}
|
||||||
|
new_entries
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(
|
||||||
|
side_only: &[ModEntry],
|
||||||
|
universal: &[ModEntry],
|
||||||
|
mods_dir: &PathBuf,
|
||||||
|
build_dir: &PathBuf,
|
||||||
|
name: &str,
|
||||||
|
) {
|
||||||
|
if let Err(e) = fs::create_dir_all(build_dir) {
|
||||||
|
error!("Не удалось создать папку {}: {}", name, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mods: Vec<_> = side_only.iter().chain(universal).map(|e| &e.name).collect();
|
||||||
|
let count = mods.len();
|
||||||
|
for file_name in &mods {
|
||||||
|
let src = mods_dir.join(file_name);
|
||||||
|
let dst = build_dir.join(file_name);
|
||||||
|
if src.exists() {
|
||||||
|
if let Err(e) = fs::copy(&src, &dst) {
|
||||||
|
warn!("Не удалось скопировать {}: {}", file_name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
"{} сборка создана: {} модов в {}",
|
||||||
|
name,
|
||||||
|
count,
|
||||||
|
build_dir.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_mods(mods_dir: &PathBuf) {
|
||||||
|
let mut data = load_json();
|
||||||
|
let existing_sha512: HashSet<String> = data
|
||||||
|
.client_only
|
||||||
|
.iter()
|
||||||
|
.chain(&data.server_only)
|
||||||
|
.chain(&data.universal)
|
||||||
|
.map(|e| e.sha512.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !mods_dir.exists() {
|
||||||
|
error!("Папка {} не найдена!", MODS_DIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let all_files = get_jar_files(mods_dir);
|
||||||
|
let current_names: HashSet<_> = all_files
|
||||||
|
.iter()
|
||||||
|
.filter_map(|p| p.file_name().map(|n| n.to_string_lossy().into_owned()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for cat in [
|
||||||
|
&mut data.client_only,
|
||||||
|
&mut data.server_only,
|
||||||
|
&mut data.universal,
|
||||||
|
] {
|
||||||
|
cat.retain(|e| {
|
||||||
|
if !current_names.contains(&e.name) {
|
||||||
|
info!("Удалён устаревший мод: {}", e.name);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_entries = process_new_mods(&all_files, &existing_sha512);
|
||||||
|
for (entry, cat) in new_entries {
|
||||||
|
match cat {
|
||||||
|
"client-only" => data.client_only.push(entry),
|
||||||
|
"server-only" => data.server_only.push(entry),
|
||||||
|
"universal" => data.universal.push(entry),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = save_json(&data) {
|
||||||
|
error!("Не удалось сохранить JSON: {}", e);
|
||||||
|
} else {
|
||||||
|
info!("Обновление завершено");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reassign_categories(mods_dir: &PathBuf) {
|
||||||
|
if !mods_dir.exists() {
|
||||||
|
error!("Папка {} не найдена!", MODS_DIR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let all_files = get_jar_files(mods_dir);
|
||||||
|
|
||||||
|
if PathBuf::from(JSON_FILE).exists() {
|
||||||
|
if let Err(e) = fs::remove_file(JSON_FILE) {
|
||||||
|
warn!("Не удалось удалить старый JSON: {}", e);
|
||||||
|
} else {
|
||||||
|
info!("Старый mods-list.json удалён");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = ModList {
|
||||||
|
client_only: vec![],
|
||||||
|
server_only: vec![],
|
||||||
|
universal: vec![],
|
||||||
|
};
|
||||||
|
let empty_sha512: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
|
// 1. Спрашиваем категории для всех модов
|
||||||
|
let mut temp_entries: Vec<(String, &'static str)> = vec![];
|
||||||
|
for path in &all_files {
|
||||||
|
let name = path
|
||||||
|
.file_name()
|
||||||
|
.expect("Нет имени файла")
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
let cat = ask_category(&name);
|
||||||
|
temp_entries.push((name, cat));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Вычисляем хэши параллельно (в конце)
|
||||||
|
let file_hashes = compute_hashes(&all_files);
|
||||||
|
let mut hash_map: HashMap<String, Hashes> = HashMap::new();
|
||||||
|
for (path, hashes) in file_hashes {
|
||||||
|
let name = path
|
||||||
|
.file_name()
|
||||||
|
.expect("Нет имени файла")
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
hash_map.insert(name, hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Собираем ModEntry
|
||||||
|
for (name, cat) in temp_entries {
|
||||||
|
if let Some(hashes) = hash_map.get(&name) {
|
||||||
|
let entry = ModEntry {
|
||||||
|
name: name.clone(),
|
||||||
|
sha512: hashes.sha512.clone(),
|
||||||
|
sha256: hashes.sha256.clone(),
|
||||||
|
sha1: hashes.sha1.clone(),
|
||||||
|
md5: hashes.md5.clone(),
|
||||||
|
};
|
||||||
|
match cat {
|
||||||
|
"client-only" => data.client_only.push(entry),
|
||||||
|
"server-only" => data.server_only.push(entry),
|
||||||
|
"universal" => data.universal.push(entry),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = save_json(&data) {
|
||||||
|
error!("Не удалось сохранить JSON: {}", e);
|
||||||
|
} else {
|
||||||
|
info!("Все моды перераспределены");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user