diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 1a27f30..9469e70 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -91,19 +91,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-compression" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" -dependencies = [ - "compression-codecs", - "compression-core", - "futures-io", - "pin-project-lite", - "tokio", -] - [[package]] name = "async-executor" version = "1.14.0" @@ -211,21 +198,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async_zip" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c50d65ce1b0e0cb65a785ff615f78860d7754290647d3b983208daa4f85e6" -dependencies = [ - "async-compression", - "crc32fast", - "futures-lite", - "pin-project", - "thiserror 2.0.18", - "tokio", - "tokio-util", -] - [[package]] name = "atk" version = "0.18.2" @@ -406,15 +378,6 @@ dependencies = [ "bzip2-sys", ] -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - [[package]] name = "bzip2-sys" version = "0.1.13+1.0.8" @@ -588,26 +551,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "compression-codecs" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" -dependencies = [ - "brotli", - "compression-core", - "flate2", - "memchr", - "zstd", - "zstd-safe", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -674,7 +617,7 @@ dependencies = [ "bitflags 2.11.0", "core-foundation 0.10.1", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -1205,17 +1148,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "filetime" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" -dependencies = [ - "cfg-if", - "libc", - "libredox", -] - [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1230,7 +1162,6 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", - "zlib-rs", ] [[package]] @@ -1251,6 +1182,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1258,7 +1198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1272,6 +1212,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1971,7 +1917,22 @@ dependencies = [ "tokio", "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.6", +] + +[[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 1.9.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] @@ -1992,9 +1953,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2 0.6.3", + "system-configuration 0.7.0", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2398,12 +2361,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "libbz2-rs-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" - [[package]] name = "libc" version = "0.2.184" @@ -2426,150 +2383,7 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" dependencies = [ - "bitflags 2.11.0", "libc", - "plain", - "redox_syscall 0.7.3", -] - -[[package]] -name = "lighty-auth" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d6a006a91985c36bca2b9d6210ee5207f4b2ff8ad286048e95c9c82e85908b" -dependencies = [ - "lighty-core", - "reqwest 0.12.28", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "lighty-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73d20b4f673d0a66603bf064bfbe55af72833f73ce5f432fece8800cdd090ed6" -dependencies = [ - "async-compression", - "async_zip", - "directories", - "futures-util", - "hex", - "once_cell", - "os_info", - "path-absolutize", - "reqwest 0.12.28", - "sanitize-filename", - "serde", - "serde_json", - "sha1", - "thiserror 2.0.18", - "tokio", - "tokio-tar", - "tokio-util", - "zip 6.0.0", -] - -[[package]] -name = "lighty-java" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20021964bc99be066862de99f2bdef71d5d4cee5b3b30aeb4d5d48ba22810a1e" -dependencies = [ - "lighty-core", - "path-absolutize", - "serde", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "lighty-launch" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bbd45bfd6437d35340f71b15333dfbaddbedae5f4f6fc3c87d6450ca1b89742" -dependencies = [ - "async-trait", - "async_zip", - "directories", - "futures", - "futures-util", - "hex", - "lighty-auth", - "lighty-core", - "lighty-java", - "lighty-loaders", - "lighty-version", - "once_cell", - "reqwest 0.12.28", - "sanitize-filename", - "serde", - "serde_json", - "sha1", - "thiserror 2.0.18", - "tokio", - "tokio-util", -] - -[[package]] -name = "lighty-launcher" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f5fc431668284ee95476dfd3daa4f7c4bad6390f24c2f2186c606c3ac1398c" -dependencies = [ - "directories", - "lighty-auth", - "lighty-core", - "lighty-java", - "lighty-launch", - "lighty-loaders", - "lighty-version", - "once_cell", - "tokio", -] - -[[package]] -name = "lighty-loaders" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "094559e90a9c98f17403c0f9b51678708c764b66d8ca8c2ef50d473e95324566" -dependencies = [ - "async-trait", - "async_zip", - "directories", - "futures", - "lighty-core", - "memmap2", - "once_cell", - "reqwest 0.12.28", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "version-compare", - "zip 6.0.0", -] - -[[package]] -name = "lighty-version" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5eac579ffa7cab79fb09efd233b0ec89bc372ba93a96e856799dc0dcb3f817" -dependencies = [ - "async-trait", - "directories", - "lighty-core", - "lighty-loaders", - "once_cell", - "reqwest 0.12.28", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "version-compare", ] [[package]] @@ -2624,7 +2438,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "uuid 1.23.0", - "zip 2.4.2", + "zip", ] [[package]] @@ -2637,16 +2451,6 @@ dependencies = [ "crc", ] -[[package]] -name = "lzma-rust2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a" -dependencies = [ - "crc", - "sha2", -] - [[package]] name = "lzma-sys" version = "0.1.20" @@ -2712,15 +2516,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memmap2" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -2778,6 +2573,23 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.9.0" @@ -2814,33 +2626,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "num-conv" version = "0.2.1" @@ -2921,27 +2712,6 @@ dependencies = [ "objc2-foundation", ] -[[package]] -name = "objc2-cloud-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" -dependencies = [ - "bitflags 2.11.0", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" -dependencies = [ - "objc2", - "objc2-foundation", -] - [[package]] name = "objc2-core-foundation" version = "0.3.2" @@ -2966,38 +2736,6 @@ dependencies = [ "objc2-io-surface", ] -[[package]] -name = "objc2-core-image" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-location" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-text" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" -dependencies = [ - "bitflags 2.11.0", - "objc2", - "objc2-core-foundation", - "objc2-core-graphics", -] - [[package]] name = "objc2-encode" version = "4.1.0" @@ -3021,7 +2759,6 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", "block2", - "libc", "objc2", "objc2-core-foundation", ] @@ -3056,27 +2793,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "block2", "objc2", - "objc2-cloud-kit", - "objc2-core-data", "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-core-location", - "objc2-core-text", - "objc2-foundation", - "objc2-quartz-core", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" -dependencies = [ - "objc2", "objc2-foundation", ] @@ -3112,12 +2830,50 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "openssl" +version = "0.10.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types 0.3.2", + "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 2.0.117", +] + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -3134,22 +2890,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_info" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" -dependencies = [ - "android_system_properties", - "log", - "nix", - "objc2", - "objc2-foundation", - "objc2-ui-kit", - "serde", - "windows-sys 0.61.2", -] - [[package]] name = "pango" version = "0.18.3" @@ -3199,29 +2939,11 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link 0.2.1", ] -[[package]] -name = "path-absolutize" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5" -dependencies = [ - "path-dedot", -] - -[[package]] -name = "path-dedot" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" -dependencies = [ - "once_cell", -] - [[package]] name = "pathdiff" version = "0.2.3" @@ -3431,26 +3153,6 @@ dependencies = [ "siphasher 1.0.2", ] -[[package]] -name = "pin-project" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -3474,12 +3176,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - [[package]] name = "plist" version = "1.8.0" @@ -3535,12 +3231,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppmd-rust" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -3872,15 +3562,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -3890,15 +3571,6 @@ dependencies = [ "bitflags 2.11.0", ] -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", -] - [[package]] name = "redox_users" version = "0.5.2" @@ -3988,7 +3660,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper 0.1.2", - "system-configuration", + "system-configuration 0.5.1", "tokio", "tokio-rustls 0.24.1", "tower-service", @@ -3996,7 +3668,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.4", + "webpki-roots", "winreg 0.50.0", ] @@ -4017,10 +3689,12 @@ dependencies = [ "http-body-util", "hyper 1.9.0", "hyper-rustls 0.27.7", + "hyper-tls", "hyper-util", "js-sys", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -4032,6 +3706,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", + "tokio-native-tls", "tokio-rustls 0.26.4", "tokio-util", "tower", @@ -4042,7 +3717,6 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams 0.4.2", "web-sys", - "webpki-roots 1.0.6", ] [[package]] @@ -4220,30 +3894,20 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sanitize-filename" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" -dependencies = [ - "regex", -] - [[package]] name = "saturn-launcher" version = "0.1.0" dependencies = [ - "anyhow", "directories", - "lighty-launcher", "lyceris", "once_cell", + "rayon", + "reqwest 0.12.28", "serde", "serde_json", "tauri", "tauri-build", "tauri-plugin-opener", - "tracing-subscriber", ] [[package]] @@ -4604,15 +4268,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shlex" version = "1.3.0" @@ -4694,7 +4349,7 @@ dependencies = [ "objc2-foundation", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.18", + "redox_syscall", "tracing", "wasm-bindgen", "web-sys", @@ -4861,7 +4516,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "system-configuration-sys 0.6.0", ] [[package]] @@ -4874,6 +4540,16 @@ dependencies = [ "libc", ] +[[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 = "system-deps" version = "6.2.2" @@ -5269,15 +4945,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - [[package]] name = "time" version = "0.3.47" @@ -5362,6 +5029,16 @@ dependencies = [ "syn 2.0.117", ] +[[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.24.1" @@ -5382,32 +5059,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-tar" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75" -dependencies = [ - "filetime", - "futures-core", - "libc", - "redox_syscall 0.3.5", - "tokio", - "tokio-stream", - "xattr", -] - [[package]] name = "tokio-util" version = "0.7.18" @@ -5416,7 +5067,6 @@ checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -5548,18 +5198,13 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "async-compression", "bitflags 2.11.0", "bytes", - "futures-core", "futures-util", "http 1.4.0", "http-body 1.0.1", - "http-body-util", "iri-string", "pin-project-lite", - "tokio", - "tokio-util", "tower", "tower-layer", "tower-service", @@ -5583,7 +5228,6 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5607,32 +5251,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", ] [[package]] @@ -5811,10 +5429,10 @@ dependencies = [ ] [[package]] -name = "valuable" -version = "0.1.1" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" @@ -6094,15 +5712,6 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webview2-com" version = "0.38.2" @@ -6288,6 +5897,17 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -6845,16 +6465,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "xattr" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" -dependencies = [ - "libc", - "rustix", -] - [[package]] name = "xz2" version = "0.1.7" @@ -7050,7 +6660,7 @@ checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ "aes", "arbitrary", - "bzip2 0.5.2", + "bzip2", "constant_time_eq", "crc32fast", "crossbeam-utils", @@ -7072,39 +6682,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "zip" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" -dependencies = [ - "aes", - "arbitrary", - "bzip2 0.6.1", - "constant_time_eq", - "crc32fast", - "deflate64", - "flate2", - "getrandom 0.3.4", - "hmac", - "indexmap 2.13.1", - "lzma-rust2", - "memchr", - "pbkdf2", - "ppmd-rust", - "sha1", - "time", - "zeroize", - "zopfli", - "zstd", -] - -[[package]] -name = "zlib-rs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" - [[package]] name = "zmij" version = "1.0.21" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 4383c93..98e9637 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,9 +22,8 @@ tauri = { version = "2", features = [] } tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -lighty-launcher = { version = "0.8.6", features = ["fabric"] } directories = "6.0" once_cell = "1.21" -tracing-subscriber = "0.3" -anyhow = "1.0" lyceris = "1.1.3" +rayon = "1.10.0" +reqwest = "0.12.9" diff --git a/src-tauri/src/launcher/install.rs b/src-tauri/src/launcher/install.rs new file mode 100644 index 0000000..550f66d --- /dev/null +++ b/src-tauri/src/launcher/install.rs @@ -0,0 +1,563 @@ +/// This module handles the installation of Minecraft, including downloading +/// necessary files and managing the Java runtime environment. +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use std::{ + env::consts::{ARCH, OS}, + fs::{self, create_dir_all}, + path::{Path, PathBuf, MAIN_SEPARATOR_STR}, + process::Command, +}; + +use lyceris::{ + error::Error, + http::{ + downloader::{download, download_multiple}, + fetch::fetch, + }, + json::{ + java::{JavaFileManifest, JavaManifest}, + version::{ + asset_index::AssetIndex, + manifest::VersionManifest, + meta::vanilla::{self, JavaVersion, VersionMeta}, + }, + }, + minecraft::{ + install::FileType, CLASSPATH_SEPARATOR, JAVA_MANIFEST_ENDPOINT, RESOURCES_ENDPOINT, + VERSION_MANIFEST_ENDPOINT, + }, + util::{ + extract::{extract_file, read_file_from_jar}, + hash::calculate_sha1, + json::{read_json, write_json}, + }, +}; + +use lyceris::minecraft::{ + config::Config, + emitter::Emitter, + loader::Loader, + parse::{parse_lib_path, ParseRule}, +}; + +/// Represents a file to be downloaded, including its metadata. +#[derive(Clone)] +struct DownloadFile { + file_name: String, + sha1: String, + url: String, + path: PathBuf, + r#type: FileType, +} + +/// Installs the specified version of Minecraft by downloading necessary files +/// and setting up the environment. +/// +/// # Parameters +/// - `config`: The configuration for the installation process. +/// - `emitter`: An optional emitter for logging progress. +/// +/// # Returns +/// A result indicating success or failure of the installation process. +pub async fn install( + config: &Config, + emitter: Option<&Emitter>, +) -> lyceris::Result<()> { + let manifest: VersionManifest = + fetch(VERSION_MANIFEST_ENDPOINT, config.client.as_ref()).await?; + let version_json_path = config.get_version_json_path(); + let mut meta: VersionMeta = if !version_json_path.exists() { + let mut meta = + fetch_version_meta(&manifest, &config.version, config.client.as_ref()).await?; + if let Some(loader) = &config.loader { + meta = loader.merge(&config.into_vanilla(), meta, emitter).await?; + } + write_json(&version_json_path, &meta).await?; + meta + } else { + read_json(&version_json_path).await? + }; + + let asset_index_path = config + .get_indexes_path() + .join(format!("{}.json", &meta.asset_index.id)); + let asset_index: AssetIndex = if !asset_index_path.exists() { + let asset_index = fetch(&meta.asset_index.url, config.client.as_ref()).await?; + write_json(&asset_index_path, &asset_index).await?; + asset_index + } else { + read_json(&asset_index_path).await? + }; + + let natives_path = config.get_natives_path().join(&config.version); + if !natives_path.is_dir() { + // todo async + create_dir_all(&natives_path)?; + } + + let check_natives = fs::read_dir(&natives_path)?.count() == 0; + let mut to_be_extracted = Vec::with_capacity(10); + + let default_java_version = JavaVersion::default(); + let java_version = meta.java_version.as_ref().unwrap_or(&default_java_version); + let runtime_path = config.get_runtime_path().join(&java_version.component); + + let java_manifest: JavaManifest = fetch(JAVA_MANIFEST_ENDPOINT, config.client.as_ref()).await?; + let java_url = get_java_url(&java_manifest, java_version)?; + let java_files: JavaFileManifest = fetch(java_url, config.client.as_ref()).await?; + + let file_map = build_file_map( + &asset_index, + &meta, + &java_files, + &runtime_path, + config, + check_natives, + &mut to_be_extracted, + )?; + + download_necessary( + file_map, + &config.game_dir, + asset_index.map_to_resources.unwrap_or_default() + || asset_index.r#virtual.unwrap_or_default(), + emitter, + config.client.as_ref(), + ) + .await?; + + if !to_be_extracted.is_empty() { + create_dir_all(&natives_path)?; + for extract in to_be_extracted { + if let Some(path) = extract.path { + let path = PathBuf::from(path); + download(&extract.url, &path, emitter, config.client.as_ref()).await?; + extract_file(&path, &natives_path)?; + } + } + } + + execute_processors_if_exists(&mut meta, config).await?; + + Ok(()) +} + +/// Fetches the version metadata for the specified version from the manifest. +/// +/// # Parameters +/// - `manifest`: The version manifest containing available versions. +/// - `version`: The version to fetch metadata for. +/// - `client`: An optional HTTP client for making requests. +/// +/// # Returns +/// The version metadata for the specified version. +async fn fetch_version_meta( + manifest: &VersionManifest, + version: &str, + client: Option<&reqwest::Client>, +) -> lyceris::Result { + let version_url = manifest + .versions + .iter() + .find(|v| v.id == version) + .ok_or_else(|| Error::UnknownVersion("Vanilla".to_string()))? + .url + .clone(); + fetch(&version_url, client).await +} + +/// Gets the download URL for the specified Java version based on the operating system and architecture. +/// +/// # Parameters +/// - `java_manifest`: The manifest containing Java version information. +/// - `java_version`: The specific Java version to retrieve the URL for. +/// +/// # Returns +/// The download URL for the specified Java version. +fn get_java_url( + java_manifest: &JavaManifest, + java_version: &JavaVersion, +) -> lyceris::Result { + let os = if OS == "macos" { "mac-os" } else { OS }; + let arch = match ARCH { + "x86" => { + if os == "linux" { + "i386" + } else { + "x86" + } + } + "x86_64" => "x64", + "aarch64" => "arm64", + _ => return Err(Error::UnsupportedArchitecture), + }; + let os_arch = if (os == "linux" && arch != "i386") + || (os == "mac-os" && (arch != "arm64" || java_version.major_version == 8)) + { + os.to_string() + } else { + format!("{}-{}", os, arch) + }; + java_manifest + .get(&os_arch) + .ok_or_else(|| Error::NotFound("Java map by operating system".to_string()))? + .get(&java_version.component) + .ok_or_else(|| Error::UnknownVersion("Java version".to_string()))? + .first() + .ok_or_else(|| Error::NotFound("Java gamecore".to_string())) + .map(|entry| &entry.manifest.url) + .cloned() +} + +/// Builds a map of files to be downloaded based on the asset index, version metadata, and Java files. +/// +/// # Parameters +/// - `asset_index`: The asset index containing file information. +/// - `meta`: The version metadata. +/// - `java_files`: The Java file manifest. +/// - `runtime_path`: The path to the Java runtime. +/// - `config`: The configuration for the installation process. +/// - `check_natives`: A flag indicating whether to check for native files. +/// - `to_be_extracted`: A mutable vector to store files that need to be extracted. +/// +/// # Returns +/// A vector of `DownloadFile` representing the files to be downloaded. +fn build_file_map( + asset_index: &AssetIndex, + meta: &VersionMeta, + java_files: &JavaFileManifest, + runtime_path: &Path, + config: &Config, + check_natives: bool, + to_be_extracted: &mut Vec, +) -> lyceris::Result> { + let version_jar_path = config.get_version_jar_path(); + let version_download = if !version_jar_path.exists() + || !calculate_sha1(&version_jar_path)?.eq(&meta.downloads.client.sha1) + { + Some(DownloadFile { + file_name: version_jar_path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + r#type: FileType::Library, + path: version_jar_path, + sha1: meta.downloads.client.sha1.clone(), + url: meta.downloads.client.url.clone(), + }) + } else { + None + }; + + let asset_files = asset_index + .objects + .iter() + .map(|(key, meta)| { + let assets_path = config.get_assets_path(); + let hash = &meta.hash; + DownloadFile { + file_name: key.clone(), + sha1: hash.clone(), + url: format!("{}/{}/{}", RESOURCES_ENDPOINT, &hash[0..2], hash), + path: assets_path.join("objects").join(&hash[0..2]).join(hash), + r#type: FileType::Asset { + is_map: asset_index.map_to_resources.unwrap_or_default(), + is_virtual: asset_index.r#virtual.unwrap_or_default(), + }, + } + }) + .collect::>(); + + let library_files = meta + .libraries + .iter() + .filter_map(|lib| { + if !lib.rules.parse_rule() { + return None; + } + let downloads = lib.downloads.as_ref()?; + if check_natives { + if let Some(classifiers) = &downloads.classifiers { + let classifier = match OS { + "windows" => &classifiers.natives_windows, + "linux" => &classifiers.natives_linux, + "macos" => &classifiers.natives_macos, + _ => return None, + }; + if let Some(classifier) = classifier { + if let Some(classifier_path) = &classifier.path { + let path = config + .game_dir + .join("libraries") + .join(classifier_path.replace("/", MAIN_SEPARATOR_STR)); + let url = classifier.url.clone(); + let sha1 = classifier.sha1.clone(); + to_be_extracted.push(vanilla::File { + path: Some(path.to_string_lossy().into_owned()), + sha1: sha1.clone(), + size: classifier.size, + url: url.clone(), + }); + return Some(DownloadFile { + file_name: PathBuf::from(url.clone()) + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + sha1, + url, + path, + r#type: FileType::Library, + }); + } + } + } + } + let artifact = downloads.artifact.as_ref()?; + Some(DownloadFile { + file_name: PathBuf::from(artifact.url.clone()) + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + sha1: artifact.sha1.clone(), + url: artifact.url.clone(), + path: config + .game_dir + .join("libraries") + .join(artifact.path.as_ref()?.replace("/", MAIN_SEPARATOR_STR)), + r#type: FileType::Library, + }) + }) + .collect::>(); + + let java_files = java_files + .files + .iter() + .filter_map(|(name, file)| { + let path = runtime_path.join(name.replace("/", MAIN_SEPARATOR_STR)); + file.downloads.as_ref().map(|downloads| DownloadFile { + file_name: name + .split(MAIN_SEPARATOR_STR) + .last() + .unwrap_or(name) + .to_string(), + path, + sha1: downloads.raw.sha1.clone(), + url: downloads.raw.url.clone(), + r#type: FileType::Java, + }) + }) + .collect::>(); + + Ok([ + version_download.into_iter().collect::>(), + asset_files, + library_files, + java_files, + ] + .concat()) +} + +/// Executes any processors defined in the version metadata, if they exist. +/// +/// # Parameters +/// - `meta`: The version metadata containing processor information. +/// - `config`: The configuration for the installation process. +/// +/// # Returns +/// A result indicating success or failure of the processor execution. +async fn execute_processors_if_exists( + meta: &mut VersionMeta, + config: &Config, +) -> lyceris::Result<()> { + if let Some(ref mut processors) = meta.processors { + let data = meta + .data + .as_ref() + .ok_or_else(|| Error::NotFound("Forge Installer Data".to_string()))?; + + let libraries_path = config.get_libraries_path(); + + for processor in processors { + if let Some(sides) = &processor.sides { + if !sides.contains(&"client".to_string()) { + continue; + } + } + + if processor.success { + continue; + } + + let classpath = processor + .classpath + .iter() + .filter_map(|arg| { + Some( + libraries_path + .join(parse_lib_path(arg).ok()?) + .to_string_lossy() + .into_owned(), + ) + }) + .collect::>() + .join(CLASSPATH_SEPARATOR); + + let main_class = read_file_from_jar( + &libraries_path + .join(parse_lib_path(&processor.jar)?) + .to_string_lossy() + .into_owned(), + "META-INF/MANIFEST.MF", + )? + .lines() + .find(|line| line.starts_with("Main-Class:")) + .ok_or_else(|| Error::NotFound("Main-Class of processor".to_string()))? + .split(":") + .last() + .ok_or_else(|| Error::NotFound("Main-Class of processor".to_string()))? + .trim() + .to_string(); + + let args = processor + .args + .iter() + .map(|arg| { + let trimmed_arg = &arg[1..arg.len() - 1]; + if arg.starts_with('{') { + if let Some(entry) = data.get(trimmed_arg) { + if entry.client.starts_with('[') { + if let Ok(parsed_path) = + parse_lib_path(&entry.client[1..entry.client.len() - 1]) + { + return libraries_path + .join(parsed_path) + .to_string_lossy() + .into_owned(); + } + } + return entry.client.clone(); + } + } else if arg.starts_with('[') { + if let Ok(parsed_path) = parse_lib_path(trimmed_arg) { + return libraries_path + .join(parsed_path) + .to_string_lossy() + .into_owned(); + } + } + + arg.clone() + }) + .collect::>(); + + let child = Command::new( + config + .get_java_path( + meta.java_version + .as_ref() + .unwrap_or(&JavaVersion::default()), + ) + .await?, + ) + .arg("-cp") + .arg(format!( + "{}{}{}", + classpath, + CLASSPATH_SEPARATOR, + libraries_path + .join(parse_lib_path(&processor.jar)?) + .to_string_lossy() + .into_owned() + )) + .arg(main_class) + .args(args) + .output()?; + + if child.status.success() { + processor.success = true; + } else { + return Err(Error::Fail(format!( + "Processor failed: {}", + String::from_utf8_lossy(&child.stderr) + ))); + } + } + } + + write_json(&config.get_version_json_path(), &meta).await?; + + Ok(()) +} + +/// Downloads the necessary files based on the provided file list. +/// +/// # Parameters +/// - `files`: A vector of files to be downloaded. +/// - `game_dir`: The directory where the game is installed. +/// - `legacy`: A flag indicating whether to handle legacy assets. +/// - `emitter`: An optional emitter for logging progress. +/// - `client`: An optional HTTP client for making requests. +/// +/// # Returns +/// A result indicating success or failure of the download process. +async fn download_necessary( + files: Vec, + game_dir: &Path, + legacy: bool, + emitter: Option<&Emitter>, + client: Option<&reqwest::Client>, +) -> lyceris::Result<()> { + let broken_ones: Vec<(String, PathBuf, FileType)> = files + .par_iter() + .filter_map(|file| { + if file.url.is_empty() { + return None; + } + if !file.path.exists() + || (!file.sha1.is_empty() && calculate_sha1(&file.path).ok()? != file.sha1) + { + return Some((file.url.clone(), file.path.clone(), file.r#type.clone())); + } + None + }) + .collect(); + + download_multiple(broken_ones, emitter, client).await?; + + if legacy { + files.par_iter().try_for_each(|file| { + if let FileType::Asset { is_virtual, is_map } = file.r#type { + let target_path = if is_virtual { + game_dir + .join("assets") + .join("virtual") + .join("legacy") + .join(&file.file_name) + } else if is_map { + game_dir.join("resources").join(&file.file_name) + } else { + return None::<()>; + }; + + if let Some(parent) = target_path.parent() { + if !parent.is_dir() { + fs::create_dir_all(parent).ok(); + } + } + + if !target_path.exists() || calculate_sha1(&target_path).ok()? != file.sha1 { + fs::copy(&file.path, &target_path).ok(); + } + + return None; + } + + None + }); + } + + Ok(()) +} diff --git a/src-tauri/src/launcher.rs b/src-tauri/src/launcher/mod.rs similarity index 94% rename from src-tauri/src/launcher.rs rename to src-tauri/src/launcher/mod.rs index d1d21f5..784a5a8 100644 --- a/src-tauri/src/launcher.rs +++ b/src-tauri/src/launcher/mod.rs @@ -1,13 +1,14 @@ +pub mod install; + use directories::ProjectDirs; use lyceris::minecraft::{ config::ConfigBuilder, emitter::{Emitter, Event}, - install::install, launch::launch, }; use once_cell::sync::Lazy; -static LAUNCHER_DIR: Lazy = Lazy::new(|| { +pub static LAUNCHER_DIR: Lazy = Lazy::new(|| { ProjectDirs::from("org", "SaturnLauncher", "SaturnLauncher") .expect("Failed to create project directories") }); @@ -66,7 +67,7 @@ pub async fn launch_game() -> Result<(), String> { // Install method also checks for broken files // and downloads them again if they are broken. - install(&config, Some(&emitter)) + install::install(&config, Some(&emitter)) .await .map_err(|e| e.to_string())?;