commit d647e84db069455cf28e118f2174a1507e8ce945 Author: Blank038 Date: Sat Nov 29 17:31:00 2025 +0800 feat: Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b6e2d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +.idea/ +.claude/ +.qoder/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..647df74 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2257 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[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 = "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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[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 = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "emod-cli" +version = "0.1.0-dev" +dependencies = [ + "anyhow", + "clap", + "dirs", + "reqwest", + "serde", + "serde_json", + "uuid", + "walkdir", + "zip", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[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 = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[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]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[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 = "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 = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags", + "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.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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 = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "reqwest" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe060fe50f524be480214aba758c71f99f90ee8c83c5a36b5e9e1d568eb4eb3" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "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", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[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 = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +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 = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +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 = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", +] + +[[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 = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "rand", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[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 = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[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 = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "hmac", + "indexmap", + "lzma-rs", + "memchr", + "pbkdf2", + "rand", + "sha1", + "thiserror 2.0.9", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e516454 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] +resolver = "2" +members = [] + +[package] +name = "emod-cli" +version = "0.1.0-dev" +edition = "2024" + +[dependencies] +clap = { version = "4.5.32", features = ["derive"] } +reqwest = { version = "0.12", features = ["json", "blocking"] } +uuid = { version = "1.4", features = ["v4", "fast-rng", "macro-diagnostics"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +zip = "2.2.2" +walkdir = "2" +anyhow = "1.0.97" +dirs = "5.0" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b963a2d --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# emod-cli + +> 🚧 WIP 请勿使用,项目处于快速原型开发实验中。 + +## 介绍 + +基于网易我的世界组件开发的 cli 工具,用于管理组件的创建、打包等 + +## 如何使用 + +```bash +# 创建一个 Addon 项目 +emod-cli create --name <项目名> --target [目标例子] +# 打包一个 Addon 项目 +emod-cli release --path <项目路径> --version [发布版本] +``` + +## 未来计划 + +- [ ] 重构代码,使代码结构更加合理 +- [ ] 更新预设命令,可一行创建预设资源 diff --git a/examples/VARIABLES.md b/examples/VARIABLES.md new file mode 100644 index 0000000..2772a18 --- /dev/null +++ b/examples/VARIABLES.md @@ -0,0 +1,10 @@ +# VARIABLES + +示例中的变量替换 + +## 变量列表 + +|变量名|描述| +|:----|:----| +|`__mod_name__`|项目名| +|`__mod_name_lower__`|项目名小写驼峰| diff --git a/examples/default/.gitignore b/examples/default/.gitignore new file mode 100644 index 0000000..a89292c --- /dev/null +++ b/examples/default/.gitignore @@ -0,0 +1,4 @@ +.idea/ +studio.json +.mcs/editorSave.json +.mcs/images/ \ No newline at end of file diff --git a/examples/default/behavior_pack/BoxData/.gitkeep b/examples/default/behavior_pack/BoxData/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/Galaxy/Macro/.gitkeep b/examples/default/behavior_pack/Galaxy/Macro/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/Galaxy/Template/.gitkeep b/examples/default/behavior_pack/Galaxy/Template/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/Parts/.gitkeep b/examples/default/behavior_pack/Parts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/Presets/.gitkeep b/examples/default/behavior_pack/Presets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/entities/.gitkeep b/examples/default/behavior_pack/entities/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/Parts/__init__.py b/examples/default/behavior_pack/exampleScripts/Parts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/__init__.py b/examples/default/behavior_pack/exampleScripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/enums/__init__.py b/examples/default/behavior_pack/exampleScripts/enums/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/enums/serverEvents.py b/examples/default/behavior_pack/exampleScripts/enums/serverEvents.py new file mode 100644 index 0000000..09e5cf0 --- /dev/null +++ b/examples/default/behavior_pack/exampleScripts/enums/serverEvents.py @@ -0,0 +1,3 @@ +class ServerEvents(object): + BlockRandomTickServerEvent = "BlockRandomTickServerEvent" + ServerBlockEntityTickEvent = "ServerBlockEntityTickEvent" diff --git a/examples/default/behavior_pack/exampleScripts/listen/__init__.py b/examples/default/behavior_pack/exampleScripts/listen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/listen/listen.py b/examples/default/behavior_pack/exampleScripts/listen/listen.py new file mode 100644 index 0000000..64c789a --- /dev/null +++ b/examples/default/behavior_pack/exampleScripts/listen/listen.py @@ -0,0 +1,25 @@ +from mod.common.system.baseSystem import BaseSystem + + +def listen(event, namespace=None, system_name=None): + def decorator(func): + func._annotation_listen = event + func._listen_namespace = namespace + func._listen_system_name = system_name + return func + + return decorator + + +def inject_listener(instance, system, namespace, system_name): # type: (object, BaseSystem, str, str) -> None + for name, method in instance.__class__.__dict__.items(): + if (callable(method) and hasattr(method, '_annotation_listen') + and hasattr(method, '_listen_namespace') + and hasattr(method, '_listen_system_name')): + event = method._annotation_listen + anno_namespace = method._listen_namespace + anno_system_name = method._listen_system_name + final_namespace = anno_namespace if anno_system_name is not None else namespace + final_system_name = anno_system_name if anno_system_name is not None else system_name + system.ListenForEvent(final_namespace, final_system_name, event, instance, method) + print(event) diff --git a/examples/default/behavior_pack/exampleScripts/modCommon/__init__.py b/examples/default/behavior_pack/exampleScripts/modCommon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/modCommon/emodSystem.py b/examples/default/behavior_pack/exampleScripts/modCommon/emodSystem.py new file mode 100644 index 0000000..e53562d --- /dev/null +++ b/examples/default/behavior_pack/exampleScripts/modCommon/emodSystem.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +import mod.server.extraServerApi as serverApi +import mod.client.extraClientApi as clientApi + +from mod.common.system.baseSystem import BaseSystem + +from __mod_name_lower__Scripts.listen.listen import inject_listener + + +class EasyModBaseSystem(serverApi.GetServerSystemCls()): + + def __init__(self, namespace, system_name, engine_namespace, engine_system_name): + super(EasyModBaseSystem, self).__init__(namespace, system_name) + inject_listener(self.__class__, self, engine_namespace, engine_system_name) + +class EasyModServerSystem(EasyModBaseSystem, ServerSystem): + + def __init__(self, namespace, system_name, engine_namespace, engine_system_name): + super(EasyModServerSystem, self).__init__(namespace, system_name, engine_namespace, engine_system_name) + +class EasyModClientSystem(EasyModBaseSystem): diff --git a/examples/default/behavior_pack/exampleScripts/modCommon/modConfig.py b/examples/default/behavior_pack/exampleScripts/modCommon/modConfig.py new file mode 100644 index 0000000..b6d4e87 --- /dev/null +++ b/examples/default/behavior_pack/exampleScripts/modCommon/modConfig.py @@ -0,0 +1,7 @@ +ProjectName = "Circus" + +ServerSystemName = "CircusServerSystem" +ServerSystemPath = "circusScripts.modServer.serverSystem.CircusServerSystem" + +ClientSystemName = "CircusClientSystem" +ClientSystemPath = "circusScripts.modClient.clientSystem.CircusClientSystem" \ No newline at end of file diff --git a/examples/default/behavior_pack/exampleScripts/modMain.py b/examples/default/behavior_pack/exampleScripts/modMain.py new file mode 100644 index 0000000..9c048f7 --- /dev/null +++ b/examples/default/behavior_pack/exampleScripts/modMain.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from mod.common.mod import Mod +import mod.server.extraServerApi as serverApi + + +@Mod.Binding(name="__mod_name__", version="0.0.1") +class __mod_name__(object): + + def __init__(self): + print("===== init __mod_name__ mod =====") + + @Mod.InitServer() + def on_server_init(self): + print("===== init __mod_name__ server =====") + serverApi.RegisterSystem("__mod_name__", "__mod_name__ServerSystem", "__mod_name_lower__Scripts.modServer.serverSystem.__mod_name__ServerSystem") + + @Mod.DestroyServer() + def on_server_destroy(self): + pass + + @Mod.InitClient() + def on_init_client(self): + pass + + @Mod.DestroyClient() + def on_init_destroy(self): + pass diff --git a/examples/default/behavior_pack/exampleScripts/modServer/__init__.py b/examples/default/behavior_pack/exampleScripts/modServer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/exampleScripts/modServer/serverSystem.py b/examples/default/behavior_pack/exampleScripts/modServer/serverSystem.py new file mode 100644 index 0000000..26dec7b --- /dev/null +++ b/examples/default/behavior_pack/exampleScripts/modServer/serverSystem.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +import mod.server.extraServerApi as serverApi + +from __mod_name_lower__Scripts.modCommon.emodSystem import EasyModBaseSystem + +ServerSystem = serverApi.GetServerSystemCls() +compFactory = serverApi.GetEngineCompFactory() +namespace = serverApi.GetEngineNamespace() +engineSystemName = serverApi.GetEngineSystemName() + + +class __mod_name__ServerSystem(EasyModServerSystem): + + def __init__(self, namespace, systemName): + super(__mod_name__ServerSystem, self).__init__(namespace, systemName) + print("===== __mod_name__ServerSystem init =====") + diff --git a/examples/default/behavior_pack/items/.gitkeep b/examples/default/behavior_pack/items/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/loot_tables/.gitkeep b/examples/default/behavior_pack/loot_tables/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/netease_feature_rules/.gitkeep b/examples/default/behavior_pack/netease_feature_rules/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/netease_features/.gitkeep b/examples/default/behavior_pack/netease_features/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/netease_items_beh/.gitkeep b/examples/default/behavior_pack/netease_items_beh/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/pack_icon.jpg b/examples/default/behavior_pack/pack_icon.jpg new file mode 100644 index 0000000..d2fa3bc Binary files /dev/null and b/examples/default/behavior_pack/pack_icon.jpg differ diff --git a/examples/default/behavior_pack/pack_manifest.json b/examples/default/behavior_pack/pack_manifest.json new file mode 100644 index 0000000..9cacfe7 --- /dev/null +++ b/examples/default/behavior_pack/pack_manifest.json @@ -0,0 +1,31 @@ +{ + "modules": [ + { + "description": "", + "type": "data", + "uuid": "{behavior_module_uuid}", + "version": [ + 0, + 0, + 1 + ] + } + ], + "header": { + "description": "", + "name": "behavior_pack", + "uuid": "{behavior_pack_uuid}", + "version": [ + 0, + 0, + 1 + ], + "min_engine_version": [ + 1, + 18, + 0 + ] + }, + "dependencies": [], + "format_version": 1 +} \ No newline at end of file diff --git a/examples/default/behavior_pack/recipes/.gitkeep b/examples/default/behavior_pack/recipes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/spawn_rules/.gitkeep b/examples/default/behavior_pack/spawn_rules/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/storyline/level/.gitkeep b/examples/default/behavior_pack/storyline/level/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/structures/.gitkeep b/examples/default/behavior_pack/structures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/behavior_pack/trading/.gitkeep b/examples/default/behavior_pack/trading/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/animation_controllers/.gitkeep b/examples/default/resource_pack/animation_controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/animations/.gitkeep b/examples/default/resource_pack/animations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/attachables/.gitkeep b/examples/default/resource_pack/attachables/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/blocks.json b/examples/default/resource_pack/blocks.json new file mode 100644 index 0000000..9af8c1e --- /dev/null +++ b/examples/default/resource_pack/blocks.json @@ -0,0 +1,7 @@ +{ + "format_version": [ + 1, + 1, + 0 + ] +} \ No newline at end of file diff --git a/examples/default/resource_pack/effects/.gitkeep b/examples/default/resource_pack/effects/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/entity/.gitkeep b/examples/default/resource_pack/entity/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/font/.gitkeep b/examples/default/resource_pack/font/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/materials/.gitkeep b/examples/default/resource_pack/materials/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/animation/.gitkeep b/examples/default/resource_pack/models/animation/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/editor_materials/.gitkeep b/examples/default/resource_pack/models/editor_materials/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/effect/.gitkeep b/examples/default/resource_pack/models/effect/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/geometry/.gitkeep b/examples/default/resource_pack/models/geometry/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/mesh/.gitkeep b/examples/default/resource_pack/models/mesh/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/netease_block/.gitkeep b/examples/default/resource_pack/models/netease_block/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/models/skeleton/invisible_bind_skeleton.json b/examples/default/resource_pack/models/skeleton/invisible_bind_skeleton.json new file mode 100644 index 0000000..fb45f00 --- /dev/null +++ b/examples/default/resource_pack/models/skeleton/invisible_bind_skeleton.json @@ -0,0 +1,56 @@ +{ + "skeleton": [ + { + "name": "root", + "parent": "root", + "initpos": [ + 0.0, + 0.0, + -0.0 + ], + "initquaternion": [ + -0.0000016026669982238673, + -0.0000015070728522914579, + 0.697554349899292, + 0.7165318727493286 + ], + "initscale": [ + 1.0, + 1.0, + 1.0 + ], + "offmtx": [ + [ + 0.026835869997739793, + 0.9996398091316223, + -1.1985919456947159e-7, + -2.22485263889673e-17 + ], + [ + -0.9996398091316223, + 0.026835869997739793, + -0.000004400427314976696, + -8.168171115038121e-16 + ], + [ + -0.000004395626092446037, + 2.3790533987266827e-7, + 1.0, + 1.856222120455442e-10 + ] + ] + } + ], + "boundingbox": [ + [ + -0.007048234809190035, + -7.17939763195119e-10, + -0.012045030482113362 + ], + [ + 0.007048234809190035, + 0.01892676018178463, + 0.012045029550790787 + ] + ] +} \ No newline at end of file diff --git a/examples/default/resource_pack/pack_icon.jpg b/examples/default/resource_pack/pack_icon.jpg new file mode 100644 index 0000000..d2fa3bc Binary files /dev/null and b/examples/default/resource_pack/pack_icon.jpg differ diff --git a/examples/default/resource_pack/pack_manifest.json b/examples/default/resource_pack/pack_manifest.json new file mode 100644 index 0000000..7f67a0a --- /dev/null +++ b/examples/default/resource_pack/pack_manifest.json @@ -0,0 +1,30 @@ +{ + "format_version": 1, + "header": { + "description": "", + "min_engine_version": [ + 1, + 18, + 0 + ], + "name": "resource_pack", + "uuid": "{resource_pack_uuid}", + "version": [ + 0, + 0, + 1 + ] + }, + "modules": [ + { + "description": "", + "type": "resources", + "uuid": "{resource_module_uuid}", + "version": [ + 0, + 0, + 1 + ] + } + ] +} \ No newline at end of file diff --git a/examples/default/resource_pack/render_controllers/.gitkeep b/examples/default/resource_pack/render_controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/shaders/glsl/.gitkeep b/examples/default/resource_pack/shaders/glsl/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/sounds/.gitkeep b/examples/default/resource_pack/sounds/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/texts/zh_CN.lang b/examples/default/resource_pack/texts/zh_CN.lang new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/textures/blocks/custom_brah.png b/examples/default/resource_pack/textures/blocks/custom_brah.png new file mode 100644 index 0000000..89dbd8d Binary files /dev/null and b/examples/default/resource_pack/textures/blocks/custom_brah.png differ diff --git a/examples/default/resource_pack/textures/blocks/custom_dirt.png b/examples/default/resource_pack/textures/blocks/custom_dirt.png new file mode 100644 index 0000000..2af9958 Binary files /dev/null and b/examples/default/resource_pack/textures/blocks/custom_dirt.png differ diff --git a/examples/default/resource_pack/textures/entity/.gitkeep b/examples/default/resource_pack/textures/entity/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/textures/items/custom_apple.png b/examples/default/resource_pack/textures/items/custom_apple.png new file mode 100644 index 0000000..c25b517 Binary files /dev/null and b/examples/default/resource_pack/textures/items/custom_apple.png differ diff --git a/examples/default/resource_pack/textures/models/.gitkeep b/examples/default/resource_pack/textures/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/textures/particle/.gitkeep b/examples/default/resource_pack/textures/particle/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/textures/sfxs/custom_sun.png b/examples/default/resource_pack/textures/sfxs/custom_sun.png new file mode 100644 index 0000000..197832c Binary files /dev/null and b/examples/default/resource_pack/textures/sfxs/custom_sun.png differ diff --git a/examples/default/resource_pack/textures/terrain_texture.json b/examples/default/resource_pack/textures/terrain_texture.json new file mode 100644 index 0000000..f98ab0d --- /dev/null +++ b/examples/default/resource_pack/textures/terrain_texture.json @@ -0,0 +1,7 @@ +{ + "resource_pack_name": "vanilla", + "texture_data": { + + }, + "texture_name": "atlas.terrain" +} \ No newline at end of file diff --git a/examples/default/resource_pack/textures/ui/.gitkeep b/examples/default/resource_pack/textures/ui/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/resource_pack/ui/.gitkeep b/examples/default/resource_pack/ui/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/examples/default/world_behavior_packs.json b/examples/default/world_behavior_packs.json new file mode 100644 index 0000000..edf507d --- /dev/null +++ b/examples/default/world_behavior_packs.json @@ -0,0 +1,11 @@ +[ + { + "pack_id": "{behavior_pack_uuid}", + "type": "Addon", + "version": [ + 0, + 0, + 1 + ] + } +] \ No newline at end of file diff --git a/examples/default/world_resource_packs.json b/examples/default/world_resource_packs.json new file mode 100644 index 0000000..3dfd670 --- /dev/null +++ b/examples/default/world_resource_packs.json @@ -0,0 +1,11 @@ +[ + { + "pack_id": "{resource_pack_uuid}", + "type": "Addon", + "version": [ + 0, + 0, + 1 + ] + } +] \ No newline at end of file diff --git a/src/commands/components.rs b/src/commands/components.rs new file mode 100644 index 0000000..514cf47 --- /dev/null +++ b/src/commands/components.rs @@ -0,0 +1,222 @@ +use crate::commands::ComponentsArgs; +use crate::entity; +use crate::utils::file; +use crate::error::Result; +use serde_json::{json, to_string_pretty}; +use std::fs; +use std::path::PathBuf; + +const COMPONENT_3D_ITEM: &str = "3ditem"; + +pub fn execute(args: &ComponentsArgs) { + if let Err(e) = run_components(args) { + eprintln!("错误: {}", e); + return; + } + println!("成功: 组件已创建"); +} + +fn run_components(args: &ComponentsArgs) -> Result<()> { + let project_path = file::find_project_dir(&args.path)?; + + validate_input_files(&args.geo, &args.texture)?; + + let identifier = args.identifier.as_deref().unwrap_or("unknown"); + + match args.component.as_str() { + COMPONENT_3D_ITEM => create_3dmodel( + args.geo.as_deref().unwrap_or("./model.geo.json"), + args.texture.as_deref().unwrap_or("./texture.png"), + identifier, + &project_path + ), + _ => Err(crate::error::CliError::NotFound( + format!("组件 '{}' 不存在", args.component) + )), + } +} + +fn validate_input_files(geo: &Option, texture: &Option) -> Result<()> { + let geo_path = geo.as_deref().unwrap_or("./model.geo.json"); + let texture_path = texture.as_deref().unwrap_or("./texture.png"); + + if !PathBuf::from(geo_path).exists() { + return Err(crate::error::CliError::NotFound( + format!("几何文件 {} 不存在", geo_path) + )); + } + + if !PathBuf::from(texture_path).exists() { + return Err(crate::error::CliError::NotFound( + format!("材质文件 {} 不存在", texture_path) + )); + } + + Ok(()) +} + +fn create_3dmodel( + geo: &str, + texture: &str, + identifier: &str, + project_path: &PathBuf, +) -> Result<()> { + let project_info = entity::get_current_release_info(&project_path)?; + + let beh_path = project_path.join(format!( + "behavior_pack_{}", + project_info.behavior_identifier + )); + let res_path = project_path.join(format!( + "resource_pack_{}", + project_info.resource_identifier + )); + + create_item_files(&beh_path, &res_path, identifier)?; + copy_assets(&res_path, geo, texture, identifier)?; + create_attachable_file(&res_path, identifier)?; + + Ok(()) +} + +fn create_item_files(beh_path: &PathBuf, res_path: &PathBuf, identifier: &str) -> Result<()> { + let behavior_item = create_behavior_item_json(identifier); + let resource_item = create_resource_item_json(identifier); + + let f_identifier = identifier.replace(":", "_"); + + let items_beh_dir = beh_path.join("netease_items_beh"); + let items_res_dir = res_path.join("netease_items_res"); + + fs::create_dir_all(&items_beh_dir)?; + fs::create_dir_all(&items_res_dir)?; + + let beh_item_path = items_beh_dir.join(format!("{}.json", f_identifier)); + let res_item_path = items_res_dir.join(format!("{}.json", f_identifier)); + + fs::write(&beh_item_path, to_string_pretty(&behavior_item)?)?; + fs::write(&res_item_path, to_string_pretty(&resource_item)?)?; + + Ok(()) +} + +fn create_behavior_item_json(identifier: &str) -> serde_json::Value { + json!({ + "format_version": "1.10", + "minecraft:item": { + "components": { + "minecraft:max_damage": 10, + "netease:armor": { + "armor_slot": 3, + "defense": 20, + "enchantment": 10 + } + }, + "description": { + "category": "Equipment", + "identifier": identifier, + "register_to_create_menu": true + } + } + }) +} + +fn create_resource_item_json(identifier: &str) -> serde_json::Value { + json!({ + "format_version": "1.10", + "minecraft:item": { + "components": { + "minecraft:icon": identifier + }, + "description": { + "category": "Equipment", + "identifier": identifier, + "register_to_create_menu": true + } + } + }) +} + +fn copy_assets( + res_path: &PathBuf, + geo: &str, + texture: &str, + identifier: &str, +) -> Result<()> { + let f_identifier = identifier.replace(":", "_"); + + copy_texture(res_path, texture, &f_identifier)?; + copy_geometry(res_path, geo, identifier, &f_identifier)?; + + Ok(()) +} + +fn copy_texture(res_path: &PathBuf, texture: &str, f_identifier: &str) -> Result<()> { + let texture_dir = res_path.join("textures/models"); + fs::create_dir_all(&texture_dir)?; + + let target_texture = texture_dir.join(format!("{}.png", f_identifier)); + fs::copy(texture, target_texture)?; + + Ok(()) +} + +fn copy_geometry( + res_path: &PathBuf, + geo: &str, + identifier: &str, + f_identifier: &str, +) -> Result<()> { + let geo_dir = res_path.join("models/entity"); + fs::create_dir_all(&geo_dir)?; + + let mut geo_value = file::read_file_to_json(&PathBuf::from(geo))?; + + let geo_name = format!("geometry.{}", identifier.replace(":", ".")); + geo_value["format_version"] = json!("1.12.0"); + geo_value["minecraft:geometry"][0]["description"]["identifier"] = json!(geo_name); + + let target_geo = geo_dir.join(format!("{}.geo.json", f_identifier)); + file::write_json_to_file(&target_geo, &geo_value)?; + + Ok(()) +} + +fn create_attachable_file(res_path: &PathBuf, identifier: &str) -> Result<()> { + let attachable_dir = res_path.join("attachables"); + fs::create_dir_all(&attachable_dir)?; + + let f_identifier = identifier.replace(":", "_"); + let geo_name = identifier.replace(":", "."); + + let attachable = json!({ + "format_version": "1.10.0", + "minecraft:attachable": { + "description": { + "geometry": { + "default": format!("geometry.{}", &geo_name) + }, + "identifier": identifier, + "materials": { + "default": "armor", + "enchanted": "armor_enchanted" + }, + "render_controllers": [ + "controller.render.armor" + ], + "scripts": { + "parent_setup": "variable.chest_layer_visible = 0.0;" + }, + "textures": { + "default": format!("textures/models/{}", &f_identifier), + "enchanted": "textures/misc/enchanted_item_glint" + } + } + } + }); + + let target_file = attachable_dir.join(format!("{}.json", &f_identifier)); + fs::write(target_file, to_string_pretty(&attachable)?)?; + + Ok(()) +} diff --git a/src/commands/create.rs b/src/commands/create.rs new file mode 100644 index 0000000..4e954b2 --- /dev/null +++ b/src/commands/create.rs @@ -0,0 +1,183 @@ +use crate::{ + config::Config, + entity::project::ProjectInfo, + utils::{file, http::HttpClient}, +}; +use std::{fs, path::PathBuf}; + +use crate::commands::CreateArgs; +use crate::error::Result; +use crate::utils::git; +use uuid::Uuid; + +pub fn execute(args: &CreateArgs, temp_dir: &PathBuf) { + if let Err(e) = create_project(&args.name, args.target.as_deref(), temp_dir) { + eprintln!("错误: {}", e); + return; + } + println!("成功: 项目已创建"); +} + +fn create_project(name: &str, target: Option<&str>, temp_dir: &PathBuf) -> Result<()> { + let target = target.unwrap_or("default"); + + check_example_exists(target)?; + + let local_dir = PathBuf::from(format!("./{}", name)); + fs::create_dir(&local_dir)?; + + clone_and_copy_template(target, temp_dir, &local_dir)?; + + initialize_project(&local_dir, name)?; + + Ok(()) +} + +fn check_example_exists(target: &str) -> Result<()> { + let check_url = format!( + "https://api.github.com/repos/AiYo-Studio/emod-cli/contents/examples/{}", + target + ); + + let client = if cfg!(debug_assertions) { + HttpClient::new_with_proxy("http://127.0.0.1:1080")? + } else { + HttpClient::new()? + }; + + let resp = client.get(&check_url)?; + + if !resp.status().is_success() { + return Err(crate::error::CliError::NotFound(format!( + "示例模板 '{}' 不存在", + target + ))); + } + + Ok(()) +} + +fn clone_and_copy_template(target: &str, temp_dir: &PathBuf, local_dir: &PathBuf) -> Result<()> { + let _ = fs::remove_dir_all(format!("{}/tmp", temp_dir.display())); + + let config = Config::load(); + let url = &config.repo_url; + git::clone_remote_project(url.to_string(), temp_dir)?; + + let target_dir = PathBuf::from(format!("{}/tmp/examples/{}", temp_dir.display(), target)); + file::copy_folder(&target_dir, local_dir)?; + + Ok(()) +} + +fn initialize_project(local_dir: &PathBuf, name: &str) -> Result<()> { + let lower_name = format!( + "{}{}", + name.chars().next().unwrap().to_lowercase(), + &name[1..] + ); + + println!("项目名称: {}", name); + println!("标识名称: {}", lower_name); + + let scripts_dir = local_dir.join(format!("behavior_pack/{}Scripts", lower_name)); + fs::rename(local_dir.join("behavior_pack/exampleScripts"), &scripts_dir)?; + + let project_info = generate_project_info(name, &lower_name); + + apply_project_info(local_dir, &scripts_dir, &project_info)?; + + rename_pack_folders(local_dir, &project_info)?; + + Ok(()) +} + +fn generate_project_info(name: &str, lower_name: &str) -> ProjectInfo { + ProjectInfo { + name: name.to_string(), + lower_name: lower_name.to_string(), + behavior_pack_uuid: Uuid::new_v4().to_string(), + resource_pack_uuid: Uuid::new_v4().to_string(), + behavior_module_uuid: Uuid::new_v4().to_string(), + resource_module_uuid: Uuid::new_v4().to_string(), + } +} + +fn apply_project_info( + local_dir: &PathBuf, + scripts_dir: &PathBuf, + info: &ProjectInfo, +) -> Result<()> { + let manifest_files = vec![ + local_dir.join("world_behavior_packs.json"), + local_dir.join("world_resource_packs.json"), + local_dir.join("behavior_pack/pack_manifest.json"), + local_dir.join("resource_pack/pack_manifest.json"), + ]; + + for path in manifest_files { + apply_info_to_json(&path, info)?; + } + + process_python_files(scripts_dir, info)?; + + Ok(()) +} + +fn apply_info_to_json(path: &PathBuf, info: &ProjectInfo) -> Result<()> { + println!(" - 修改文件: {}", path.display()); + file::update_json_file(path, |json| { + let content = serde_json::to_string(json)?; + let updated = content + .replace("{behavior_pack_uuid}", &info.behavior_pack_uuid) + .replace("{resource_pack_uuid}", &info.resource_pack_uuid) + .replace("{behavior_module_uuid}", &info.behavior_module_uuid) + .replace("{resource_module_uuid}", &info.resource_module_uuid) + .replace("__mod_name__", &info.name) + .replace("__mod_name_lower__", &info.lower_name); + *json = serde_json::from_str(&updated)?; + Ok(()) + }) +} + +fn process_python_files(dir: &PathBuf, info: &ProjectInfo) -> Result<()> { + if !dir.exists() { + return Ok(()); + } + + if dir.is_dir() { + for entry in fs::read_dir(dir)? { + let entry = entry?; + process_python_files(&entry.path(), info)?; + } + } else if dir.extension().and_then(|s| s.to_str()) == Some("py") { + apply_info_to_python(dir, info)?; + } + + Ok(()) +} + +fn apply_info_to_python(path: &PathBuf, info: &ProjectInfo) -> Result<()> { + let content = fs::read_to_string(path)?; + let updated = content + .replace("__mod_name__", &info.name) + .replace("__mod_name_lower__", &info.lower_name); + fs::write(path, updated)?; + Ok(()) +} + +fn rename_pack_folders(local_dir: &PathBuf, info: &ProjectInfo) -> Result<()> { + let behavior_suffix: String = info.behavior_pack_uuid.chars().take(8).collect(); + let resource_suffix: String = info.resource_pack_uuid.chars().take(8).collect(); + + fs::rename( + local_dir.join("behavior_pack"), + local_dir.join(format!("behavior_pack_{}", behavior_suffix)), + )?; + fs::rename( + local_dir.join("resource_pack"), + local_dir.join(format!("resource_pack_{}", resource_suffix)), + )?; + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..281043a --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,68 @@ +use clap::{arg, Args, Parser, Subcommand}; + +pub mod components; +pub mod create; +pub mod release; + +#[derive(Parser)] +#[command( + name = "emod-cli", + version = "1.0.0", + about = "Convenient Management of NetEase Minecraft Mod Project", + allow_external_subcommands = true, + long_about = None, + propagate_version = true +)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Release a new version + Release(ReleaseArgs), + /// Create a new mod project + Create(CreateArgs), + /// Create a new component + Components(ComponentsArgs), +} + +#[derive(Args)] +pub struct ReleaseArgs { + /// The path of the project + #[arg(short, long)] + pub path: Option, + /// The version of the project + #[arg(short, long)] + pub ver: Option, +} + +#[derive(Args)] +pub struct CreateArgs { + /// The name of the mod + #[arg(short, long)] + pub name: String, + /// Example target, default example is 'default' + #[arg(short, long)] + pub target: Option, +} + +#[derive(Args)] +pub struct ComponentsArgs { + /// The path of the project + #[arg(short, long)] + pub path: Option, + /// The name of the component + #[arg(short, long)] + pub component: String, + /// Import the path of the geo file. + #[arg(short, long)] + pub geo: Option, + /// Import the path of the texture file. + #[arg(short, long)] + pub texture: Option, + /// The item's identifier + #[arg(short, long)] + pub identifier: Option +} diff --git a/src/commands/release.rs b/src/commands/release.rs new file mode 100644 index 0000000..6745f6d --- /dev/null +++ b/src/commands/release.rs @@ -0,0 +1,200 @@ +use std::{ + fs::{self, File}, + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +use serde_json::Value; +use walkdir; +use zip::write::SimpleFileOptions; + +use crate::commands::ReleaseArgs; +use crate::utils::file; +use crate::{entity, entity::project::ReleaseInfo}; +use crate::error::Result; + +pub fn execute(args: &ReleaseArgs) { + if let Err(e) = run_release(args) { + eprintln!("❌ 组件打包失败: {}", e); + return; + } + println!("🍀 组件打包完成"); +} + +fn run_release(args: &ReleaseArgs) -> Result<()> { + let project_dir = file::find_project_dir(&args.path)?; + let release_info = entity::get_current_release_info(&project_dir)?; + + println!("🔖 当前行为包版本: {:?}", release_info.behavior_version); + println!("🔖 当前资源包版本: {:?}", release_info.resource_version); + + release(&args.ver, &project_dir, &release_info)?; + + Ok(()) +} + +fn release( + version: &Option, + project_dir: &PathBuf, + release_info: &ReleaseInfo, +) -> Result<()> { + let new_version = calculate_version(version, &release_info.behavior_version)?; + let version_value = Value::Array(new_version.iter().map(|v| Value::from(*v)).collect()); + + println!("📦 开始打包, 版本号: {:?}", &new_version); + + update_versions(&project_dir, &release_info, &version_value)?; + + let version_str = format!("{}.{}.{}", new_version[0], new_version[1], new_version[2]); + let output_path = package_project(&project_dir, &release_info, &version_str)?; + + println!("📦 打包完成: {}", output_path.replace("\\", "/")); + Ok(()) +} + +fn calculate_version(version: &Option, current: &[u32]) -> Result> { + if let Some(ver_str) = version { + ver_str + .split(".") + .map(|s| s.parse::().map_err(|e| e.into())) + .collect() + } else { + Ok(vec![current[0], current[1], current[2] + 1]) + } +} + +fn update_versions( + project_dir: &PathBuf, + release_info: &ReleaseInfo, + version: &Value, +) -> Result<()> { + update_pack_json(&project_dir, &version)?; + update_manifest_json(&project_dir, &release_info, &version)?; + Ok(()) +} + +fn update_pack_json(project_dir: &PathBuf, version: &Value) -> Result<()> { + let paths = vec![ + project_dir.join("world_behavior_packs.json"), + project_dir.join("world_resource_packs.json"), + ]; + + for path in paths { + file::update_json_file(&path, |json| { + json[0]["version"] = version.clone(); + Ok(()) + })?; + } + + Ok(()) +} + +fn update_manifest_json( + project_dir: &PathBuf, + release_info: &ReleaseInfo, + version: &Value, +) -> Result<()> { + let behavior_dir = project_dir.join(format!( + "behavior_pack_{}", + release_info.behavior_identifier + )); + let resource_dir = project_dir.join(format!( + "resource_pack_{}", + release_info.resource_identifier + )); + + for pack_dir in [behavior_dir, resource_dir] { + let manifest_path = pack_dir.join("pack_manifest.json"); + file::update_json_file(&manifest_path, |json| { + json["header"]["version"] = version.clone(); + json["modules"][0]["version"] = version.clone(); + Ok(()) + })?; + } + + Ok(()) +} + +fn package_project( + project_dir: &PathBuf, + release_info: &ReleaseInfo, + version: &str, +) -> Result { + let output_path = format!("{}/release_{}.zip", project_dir.display(), version); + let file = fs::File::create(&output_path)?; + let mut zip = zip::ZipWriter::new(file); + + let behavior_dir = project_dir.join(format!( + "behavior_pack_{}", + release_info.behavior_identifier + )); + let resource_dir = project_dir.join(format!( + "resource_pack_{}", + release_info.resource_identifier + )); + + add_directory_to_zip(&mut zip, &project_dir, &behavior_dir)?; + add_directory_to_zip(&mut zip, &project_dir, &resource_dir)?; + + zip.finish()?; + Ok(output_path) +} + +fn add_directory_to_zip( + zip: &mut zip::ZipWriter, + project_dir: &PathBuf, + src_dir: &PathBuf, +) -> Result<()> { + if !src_dir.is_dir() { + return Err(crate::error::CliError::InvalidData( + format!("{} 不是目录", src_dir.display()) + )); + } + + if count_files(src_dir)? == 0 { + return Ok(()); + } + + let options = SimpleFileOptions::default(); + let mut buffer = Vec::new(); + + for entry in walkdir::WalkDir::new(src_dir) { + let entry = entry?; + let path = entry.path(); + let relative_path = path.strip_prefix(project_dir) + .map_err(|e| crate::error::CliError::InvalidData(e.to_string()))?; + + let path_str = relative_path + .to_str() + .ok_or_else(|| crate::error::CliError::InvalidData( + format!("{:?} 不是有效的 UTF-8 路径", relative_path) + ))?; + + if path.is_file() { + if path_str.ends_with(".gitkeep") { + continue; + } + zip.start_file(path_str, options)?; + let mut f = File::open(path)?; + f.read_to_end(&mut buffer)?; + zip.write_all(&buffer)?; + buffer.clear(); + } else if !relative_path.as_os_str().is_empty() && count_files(path)? > 0 { + zip.add_directory(path_str, options)?; + } + } + + Ok(()) +} + +fn count_files(dir: &Path) -> Result { + let mut count = 0; + for entry in walkdir::WalkDir::new(dir) { + let entry = entry?; + if entry.path().is_file() + && !entry.path().display().to_string().ends_with(".gitkeep") { + count += 1; + } + } + Ok(count) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..76d1a7d --- /dev/null +++ b/src/config.rs @@ -0,0 +1,40 @@ +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct Config { + #[serde(default = "default_repo_url")] + pub repo_url: String, +} + +fn default_repo_url() -> String { + "https://github.com/AiYo-Studio/emod-cli.git".to_string() +} + +impl Default for Config { + fn default() -> Self { + Self { + repo_url: default_repo_url(), + } + } +} + +impl Config { + pub fn load() -> Self { + let config_path = Self::config_path(); + + if let Ok(content) = fs::read_to_string(&config_path) { + if let Ok(config) = serde_json::from_str(&content) { + return config; + } + } + + Self::default() + } + + fn config_path() -> PathBuf { + let home = dirs::home_dir().expect("无法获取用户主目录"); + home.join(".emod-cli.json") + } +} diff --git a/src/entity/mod.rs b/src/entity/mod.rs new file mode 100644 index 0000000..8436af3 --- /dev/null +++ b/src/entity/mod.rs @@ -0,0 +1,50 @@ +use crate::entity::project::ReleaseInfo; +use crate::error::Result; +use crate::utils::file::read_file_to_json; +use std::path::PathBuf; + +pub mod project; + +pub fn get_current_release_info(project_dir: &PathBuf) -> Result { + let behavior_path = project_dir.join("world_behavior_packs.json"); + let resource_path = project_dir.join("world_resource_packs.json"); + + let behavior_json = read_file_to_json(&behavior_path)?; + let resource_json = read_file_to_json(&resource_path)?; + + let behavior_version = parse_version_array(&behavior_json[0]["version"])?; + let resource_version = parse_version_array(&resource_json[0]["version"])?; + + let behavior_pack_uuid = behavior_json[0]["pack_id"] + .as_str() + .ok_or_else(|| crate::error::CliError::InvalidData("无效的 behavior pack_id".into()))? + .to_string(); + + let resource_pack_uuid = resource_json[0]["pack_id"] + .as_str() + .ok_or_else(|| crate::error::CliError::InvalidData("无效的 resource pack_id".into()))? + .to_string(); + + let behavior_identifier: String = behavior_pack_uuid.chars().take(8).collect(); + let resource_identifier: String = resource_pack_uuid.chars().take(8).collect(); + + Ok(ReleaseInfo { + behavior_version, + resource_version, + behavior_identifier, + resource_identifier, + }) +} + +fn parse_version_array(value: &serde_json::Value) -> Result> { + value + .as_array() + .ok_or_else(|| crate::error::CliError::InvalidData("版本格式无效".into()))? + .iter() + .map(|v| { + v.as_u64() + .map(|n| n as u32) + .ok_or_else(|| crate::error::CliError::InvalidData("版本号格式无效".into())) + }) + .collect() +} diff --git a/src/entity/project.rs b/src/entity/project.rs new file mode 100644 index 0000000..cb1ea59 --- /dev/null +++ b/src/entity/project.rs @@ -0,0 +1,15 @@ +pub struct ProjectInfo { + pub name: String, + pub lower_name: String, + pub behavior_pack_uuid: String, + pub resource_pack_uuid: String, + pub behavior_module_uuid: String, + pub resource_module_uuid: String, +} + +pub struct ReleaseInfo { + pub behavior_version: Vec, + pub resource_version: Vec, + pub behavior_identifier: String, + pub resource_identifier: String, +} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..0c004ec --- /dev/null +++ b/src/error.rs @@ -0,0 +1,78 @@ +use std::io; +use std::fmt; +use std::num::ParseIntError; + +#[derive(Debug)] +pub enum CliError { + Io(io::Error), + Json(serde_json::Error), + Network(reqwest::Error), + Anyhow(anyhow::Error), + Zip(zip::result::ZipError), + Walkdir(walkdir::Error), + Parse(ParseIntError), + NotFound(String), + InvalidData(String), +} + +impl fmt::Display for CliError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CliError::Io(e) => write!(f, "IO错误: {}", e), + CliError::Json(e) => write!(f, "JSON解析错误: {}", e), + CliError::Network(e) => write!(f, "网络错误: {}", e), + CliError::Anyhow(e) => write!(f, "{}", e), + CliError::Zip(e) => write!(f, "压缩错误: {}", e), + CliError::Walkdir(e) => write!(f, "目录遍历错误: {}", e), + CliError::Parse(e) => write!(f, "解析错误: {}", e), + CliError::NotFound(msg) => write!(f, "未找到: {}", msg), + CliError::InvalidData(msg) => write!(f, "无效数据: {}", msg), + } + } +} + +impl std::error::Error for CliError {} + +impl From for CliError { + fn from(err: io::Error) -> Self { + CliError::Io(err) + } +} + +impl From for CliError { + fn from(err: serde_json::Error) -> Self { + CliError::Json(err) + } +} + +impl From for CliError { + fn from(err: reqwest::Error) -> Self { + CliError::Network(err) + } +} + +impl From for CliError { + fn from(err: anyhow::Error) -> Self { + CliError::Anyhow(err) + } +} + +impl From for CliError { + fn from(err: zip::result::ZipError) -> Self { + CliError::Zip(err) + } +} + +impl From for CliError { + fn from(err: walkdir::Error) -> Self { + CliError::Walkdir(err) + } +} + +impl From for CliError { + fn from(err: ParseIntError) -> Self { + CliError::Parse(err) + } +} + +pub type Result = std::result::Result; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..92865c3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,28 @@ +mod commands; +mod entity; +mod utils; +mod error; +mod config; + +use crate::commands::{Cli, Commands}; +use clap::Parser; +use std::{env, fs, path::PathBuf}; + +fn main() { + let cli = Cli::parse(); + let temp_dir = check_temp_dir(); + match &cli.command { + Commands::Release(args) => commands::release::execute(args), + Commands::Create(args) => commands::create::execute(args, &temp_dir), + Commands::Components(args) => commands::components::execute(args), + } +} + +fn check_temp_dir() -> PathBuf { + let mut temp_dir = env::temp_dir(); + temp_dir.push("emod-cli"); + if let Err(e) = fs::create_dir_all(&temp_dir) { + eprintln!("Error: Failed to create temp directory: {}", e); + } + temp_dir +} \ No newline at end of file diff --git a/src/utils/file.rs b/src/utils/file.rs new file mode 100644 index 0000000..e87088d --- /dev/null +++ b/src/utils/file.rs @@ -0,0 +1,53 @@ +use crate::error::Result; +use serde_json::Value; +use std::{fs, path::PathBuf}; + +pub fn copy_folder(src: &PathBuf, dest: &PathBuf) -> Result<()> { + if !src.exists() || !src.is_dir() { + return Err(crate::error::CliError::NotFound(format!( + "源目录不存在: {}", + src.display() + ))); + } + if !dest.exists() { + fs::create_dir_all(dest)?; + } + for entry in fs::read_dir(src)? { + let entry = entry?; + let src_path = entry.path(); + let dest_path = dest.join(src_path.file_name().unwrap()); + if src_path.is_file() { + fs::copy(&src_path, &dest_path)?; + } else if src_path.is_dir() { + copy_folder(&src_path, &dest_path)?; + } + } + Ok(()) +} + +pub fn read_file_to_json(path: &PathBuf) -> Result { + let file = fs::read_to_string(path)?; + let json: Value = serde_json::from_str(&file)?; + Ok(json) +} + +pub fn write_json_to_file(path: &PathBuf, value: &Value) -> Result<()> { + let content = serde_json::to_string_pretty(value)?; + fs::write(path, content)?; + Ok(()) +} + +pub fn update_json_file(path: &PathBuf, updater: F) -> Result<()> +where + F: FnOnce(&mut Value) -> Result<()>, +{ + let mut json = read_file_to_json(path)?; + updater(&mut json)?; + write_json_to_file(path, &json)?; + Ok(()) +} + +pub fn find_project_dir(path: &Option) -> Result { + let path = path.as_deref().unwrap_or("."); + Ok(PathBuf::from(path)) +} diff --git a/src/utils/git.rs b/src/utils/git.rs new file mode 100644 index 0000000..3351b22 --- /dev/null +++ b/src/utils/git.rs @@ -0,0 +1,11 @@ +use std::path::PathBuf; +use crate::error::Result; + +pub fn clone_remote_project(url: String, temp_dir: &PathBuf) -> Result<()> { + std::process::Command::new("git") + .arg("clone") + .arg(url) + .arg(format!("{}/tmp", temp_dir.display())) + .output()?; + Ok(()) +} \ No newline at end of file diff --git a/src/utils/http.rs b/src/utils/http.rs new file mode 100644 index 0000000..d25a2eb --- /dev/null +++ b/src/utils/http.rs @@ -0,0 +1,26 @@ +use reqwest::blocking::Client; +use crate::error::Result; + +pub struct HttpClient { + client: Client, +} + +impl HttpClient { + pub fn new() -> Result { + let client = Client::builder().build()?; + Ok(Self { client }) + } + + pub fn new_with_proxy(proxy_url: &str) -> Result { + let proxy = reqwest::Proxy::all(proxy_url)?; + let client = Client::builder().proxy(proxy).build()?; + Ok(Self { client }) + } + + pub fn get(&self, url: &str) -> Result { + Ok(self.client + .get(url) + .header("User-Agent", "emod-cli") + .send()?) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..7da93c1 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,3 @@ +pub mod git; +pub mod file; +pub mod http; \ No newline at end of file