From f92ec382314be126a190b5ea20f886ea2fe8028d Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sat, 26 Jul 2025 17:37:11 +1000 Subject: [PATCH] feat: move to async runtime --- pages/library/[id]/index.vue | 1 + src-tauri/Cargo.lock | 1167 +++++++++++------ src-tauri/Cargo.toml | 14 +- src-tauri/src/client/autostart.rs | 22 +- src-tauri/src/client/cleanup.rs | 13 +- src-tauri/src/database/commands.rs | 26 +- src-tauri/src/database/db.rs | 102 +- src-tauri/src/database/mod.rs | 2 +- .../download_manager_builder.rs | 134 +- .../download_manager_frontend.rs | 11 +- .../src/download_manager/downloadable.rs | 21 +- src-tauri/src/games/collections/commands.rs | 70 +- src-tauri/src/games/commands.rs | 84 +- src-tauri/src/games/downloads/commands.rs | 12 +- .../src/games/downloads/download_agent.rs | 213 +-- .../src/games/downloads/download_logic.rs | 88 +- src-tauri/src/games/downloads/validate.rs | 69 +- src-tauri/src/games/library.rs | 251 ++-- src-tauri/src/games/state.rs | 4 +- src-tauri/src/lib.rs | 253 ++-- src-tauri/src/main.rs | 5 +- src-tauri/src/process/commands.rs | 30 +- src-tauri/src/process/process_manager.rs | 24 +- src-tauri/src/remote/auth.rs | 71 +- src-tauri/src/remote/cache.rs | 37 +- src-tauri/src/remote/commands.rs | 50 +- src-tauri/src/remote/fetch_object.rs | 33 +- src-tauri/src/remote/requests.rs | 8 +- src-tauri/src/remote/server_proto.rs | 6 +- src-tauri/src/remote/utils.rs | 15 +- 30 files changed, 1639 insertions(+), 1197 deletions(-) diff --git a/pages/library/[id]/index.vue b/pages/library/[id]/index.vue index 7bb7fa8..5904464 100644 --- a/pages/library/[id]/index.vue +++ b/pages/library/[id]/index.vue @@ -78,6 +78,7 @@ v-for="(url, index) in mediaUrls" :key="url" :src="url" + loading="lazy" class="absolute inset-0 w-full h-full object-cover" v-show="index === currentImageIndex" /> diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 65da817..b02f6fe 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -83,12 +83,15 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.1", + "rand 0.9.2", "raw-window-handle", "serde", "serde_repr", "tokio", "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", "zbus", ] @@ -132,7 +135,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -144,7 +147,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -156,7 +159,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -184,9 +187,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -214,7 +217,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-io", "async-lock", @@ -225,9 +228,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", @@ -236,10 +239,9 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.0.7", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -254,12 +256,18 @@ dependencies = [ ] [[package]] -name = "async-process" -version = "2.3.1" +name = "async-once-cell" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + +[[package]] +name = "async-process" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-io", "async-lock", "async-signal", @@ -268,8 +276,7 @@ dependencies = [ "cfg-if", "event-listener 5.4.0", "futures-lite", - "rustix 1.0.7", - "tracing", + "rustix 1.0.8", ] [[package]] @@ -280,14 +287,25 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "async-scoped" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4042078ea593edffc452eef14e99fdb2b120caa4ad9618bcdeabc4a023b98740" +dependencies = [ + "futures", + "pin-project", + "tokio", ] [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -295,10 +313,10 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -342,7 +360,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -393,9 +411,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -421,6 +439,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -463,7 +487,7 @@ checksum = "42b6b4cb608b8282dc3b53d0f4c9ab404655d562674c682db7e6c0458cc83c23" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -540,11 +564,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", "futures-lite", @@ -553,9 +577,9 @@ dependencies = [ [[package]] name = "boxcar" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bb12751a83493ef4b8da1120451a262554e216a247f14b48cb5e8fe7ed8bdf" +checksum = "26c4925bc979b677330a8c7fe7a8c94af2dbb4a2d37b4a20a80d884400f46baa" [[package]] name = "brotli" @@ -580,9 +604,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-tools" @@ -592,9 +616,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder" @@ -687,9 +711,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" dependencies = [ "serde", ] @@ -719,19 +743,19 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.22.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml", + "toml 0.9.2", ] [[package]] name = "cc" -version = "1.2.24" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -767,9 +791,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -908,9 +932,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -924,25 +948,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -951,9 +956,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -989,7 +994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -999,7 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1023,7 +1028,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1034,7 +1039,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1102,7 +1107,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1177,7 +1182,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1186,18 +1191,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dispatch2" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" -dependencies = [ - "bitflags 2.9.1", - "block2 0.6.1", - "libc", - "objc2 0.6.1", -] - [[package]] name = "dispatch2" version = "0.3.0" @@ -1205,6 +1198,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.9.1", + "block2 0.6.1", + "libc", "objc2 0.6.1", ] @@ -1216,7 +1211,16 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.8", ] [[package]] @@ -1239,7 +1243,7 @@ checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1257,6 +1261,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" version = "0.1.2" @@ -1270,6 +1280,9 @@ dependencies = [ name = "drop-app" version = "0.3.0-rc-8" dependencies = [ + "async-once-cell", + "async-scoped", + "async-trait", "atomic-instant-full", "bitcode", "boxcar", @@ -1277,9 +1290,11 @@ dependencies = [ "chrono", "deranged", "dirs 6.0.0", + "dropbreak", "droplet-rs", "dynfmt", "filetime", + "futures", "gethostname", "hex 0.4.3", "http 1.3.1", @@ -1289,16 +1304,14 @@ dependencies = [ "log4rs", "md5", "native_model", - "parking_lot 0.12.3", - "rand 0.9.1", - "rayon", + "parking_lot 0.12.4", + "rand 0.9.2", "regex", - "reqwest 0.12.16", + "reqwest 0.12.22", "reqwest-middleware 0.4.2", "reqwest-middleware-cache", - "rustbreak", "rustix 0.38.44", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "serde_with", @@ -1318,6 +1331,7 @@ dependencies = [ "tempfile", "throttle_my_fn", "tokio", + "tokio-util", "umu-wrapper-lib", "url", "urlencoding", @@ -1328,6 +1342,19 @@ dependencies = [ "zstd", ] +[[package]] +name = "dropbreak" +version = "2.0.0-drop" +source = "git+https://github.com/Drop-OSS/dropbreak.git#26dc631fb6bafefd47bab0c9507cf3523e0e0542" +dependencies = [ + "anyhow", + "ron", + "serde", + "tempfile", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "droplet-rs" version = "0.7.3" @@ -1392,16 +1419,16 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "embed-resource" -version = "3.0.2" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" +checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" dependencies = [ "cc", "memchr", "rustc_version", - "toml", + "toml 0.9.2", "vswhom", - "winreg 0.52.0", + "winreg 0.55.0", ] [[package]] @@ -1427,9 +1454,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -1437,13 +1464,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1473,12 +1500,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1553,9 +1580,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -1585,7 +1612,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1682,7 +1709,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1848,7 +1875,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" dependencies = [ - "rustix 1.0.7", + "rustix 1.0.8", "windows-targets 0.52.6", ] @@ -1872,7 +1899,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1968,7 +1995,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2059,14 +2086,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -2074,7 +2101,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -2083,9 +2110,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", @@ -2093,7 +2120,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -2114,9 +2141,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heck" @@ -2132,9 +2159,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2148,15 +2175,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "html5ever" version = "0.29.1" @@ -2292,14 +2310,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2315,7 +2333,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.10", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -2328,9 +2346,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.6" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", "hyper 1.6.0", @@ -2345,9 +2363,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -2361,7 +2379,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "tokio", "tower-service", "tracing", @@ -2527,12 +2545,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "serde", ] @@ -2554,6 +2572,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2710,7 +2739,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 2.9.0", + "indexmap 2.10.0", "selectors", ] @@ -2749,15 +2778,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" @@ -2770,14 +2799,24 @@ dependencies = [ ] [[package]] -name = "libredox" -version = "0.1.3" +name = "libloading" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.2", +] + +[[package]] +name = "libredox" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.15", ] [[package]] @@ -2800,9 +2839,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2840,7 +2879,7 @@ dependencies = [ "log", "log-mdc", "once_cell", - "parking_lot 0.12.3", + "parking_lot 0.12.4", "rand 0.8.5", "serde", "serde-value", @@ -2895,7 +2934,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2912,9 +2951,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap" @@ -2964,7 +3003,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2991,9 +3030,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -3006,7 +3045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -3033,9 +3072,9 @@ dependencies = [ [[package]] name = "native_model" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7050d759e3da6673361dddda4f4a743492279dd2c6484a21fbee0a8278620df0" +checksum = "50622b3adb2f3f2b8d6fddc8b030db343680c7fa7f24222a3be89e4cd725365e" dependencies = [ "anyhow", "bincode", @@ -3049,13 +3088,13 @@ dependencies = [ [[package]] name = "native_model_macro" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1577a0bebf5ed1754e240baf5d9b1845f51e598b20600aa894f55e11cd20cc6c" +checksum = "907e09bdd2fceb72bcd9b2e950c0bcb58e09d63d4ec5d6dbfa905537bbda068e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3159,23 +3198,24 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3252,7 +3292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", - "dispatch2 0.3.0", + "dispatch2", "objc2 0.6.1", ] @@ -3263,7 +3303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ "bitflags 2.9.1", - "dispatch2 0.3.0", + "dispatch2", "objc2 0.6.1", "objc2-core-foundation", "objc2-io-surface", @@ -3486,11 +3526,12 @@ dependencies = [ [[package]] name = "os_info" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fc863e2ca13dc2d5c34fb22ea4a588248ac14db929616ba65c45f21744b1e9" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" dependencies = [ "log", + "plist", "serde", "windows-sys 0.52.0", ] @@ -3549,12 +3590,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", + "parking_lot_core 0.9.11", ] [[package]] @@ -3573,13 +3614,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.15", "smallvec", "windows-targets 0.52.6", ] @@ -3716,7 +3757,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3746,6 +3787,26 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -3777,13 +3838,13 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", - "indexmap 2.9.0", - "quick-xml", + "indexmap 2.10.0", + "quick-xml 0.38.0", "serde", "time", ] @@ -3803,17 +3864,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -3871,7 +3931,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.26", + "toml_edit 0.22.27", ] [[package]] @@ -3915,9 +3975,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" dependencies = [ "memchr", ] @@ -3935,7 +4004,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -3951,7 +4020,7 @@ dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash", "rustls", @@ -3965,14 +4034,14 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -3988,9 +4057,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -4019,9 +4088,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4108,26 +4177,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "rcgen" version = "0.13.2" @@ -4153,9 +4202,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" dependencies = [ "bitflags 2.9.1", ] @@ -4182,6 +4231,26 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "reflink-copy" version = "0.1.26" @@ -4190,7 +4259,7 @@ checksum = "78c81d000a2c524133cc00d2f92f019d399e57906c3b7119271a2495354fe895" dependencies = [ "cfg-if", "libc", - "rustix 1.0.7", + "rustix 1.0.8", "windows", ] @@ -4234,7 +4303,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", @@ -4262,27 +4331,24 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.16" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf597b113be201cb2269b4c39b39a804d01b99ee95a4278f0ed04e45cff1c71" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.4.10", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", @@ -4331,7 +4397,7 @@ dependencies = [ "anyhow", "async-trait", "http 1.3.1", - "reqwest 0.12.16", + "reqwest 0.12.22", "serde", "thiserror 1.0.69", "tower-service", @@ -4359,13 +4425,13 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ "ashpd", "block2 0.6.1", - "dispatch2 0.2.0", + "dispatch2", "glib-sys", "gobject-sys", "gtk-sys", @@ -4419,33 +4485,31 @@ dependencies = [ ] [[package]] -name = "rust-ini" -version = "0.21.1" +name = "ron" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +checksum = "86018df177b1beef6c7c8ef949969c4f7cb9a9344181b92486b23c79995bdaa4" dependencies = [ - "cfg-if", - "ordered-multimap", - "trim-in-place", + "base64 0.13.1", + "bitflags 1.3.2", + "serde", ] [[package]] -name = "rustbreak" -version = "2.0.0" +name = "rust-ini" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460d97902465327d69ecfe8cefdb5972c6f94d6127ac9e992acdb51458bebc27" +checksum = "e7295b7ce3bf4806b419dc3420745998b447178b7005e2011947b38fc5aa6791" dependencies = [ - "anyhow", - "serde", - "tempfile", - "thiserror 1.0.69", + "cfg-if", + "ordered-multimap", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -4486,22 +4550,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "once_cell", "ring", @@ -4523,9 +4587,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -4568,6 +4632,30 @@ dependencies = [ "uuid", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars_derive" version = "0.8.22" @@ -4577,9 +4665,15 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.101", + "syn 2.0.104", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -4651,7 +4745,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4662,14 +4756,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" dependencies = [ "itoa", "memchr", @@ -4685,14 +4779,23 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] @@ -4711,15 +4814,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex 0.4.3", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -4729,14 +4834,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4745,7 +4850,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -4869,12 +4974,13 @@ dependencies = [ [[package]] name = "shared_child" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e297bd52991bbe0686c086957bee142f13df85d1e79b0b21630a99d374ae9dc" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" dependencies = [ "libc", - "windows-sys 0.59.0", + "sigchld", + "windows-sys 0.60.2", ] [[package]] @@ -4883,6 +4989,27 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "sigchld" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" +dependencies = [ + "libc", + "os_pipe", + "signal-hook", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.5" @@ -4912,12 +5039,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "slice-deque" @@ -4932,9 +5056,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" @@ -4946,6 +5070,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "softbuffer" version = "0.4.6" @@ -4962,7 +5096,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", "raw-window-handle", - "redox_syscall 0.5.12", + "redox_syscall 0.5.15", "wasm-bindgen", "web-sys", "windows-sys 0.59.0", @@ -5045,7 +5179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "parking_lot 0.12.3", + "parking_lot 0.12.4", "phf_shared 0.11.3", "precomputed-hash", "serde", @@ -5099,9 +5233,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -5131,7 +5265,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5173,7 +5307,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.23", "version-compare", ] @@ -5204,7 +5338,7 @@ dependencies = [ "objc2-app-kit", "objc2-foundation 0.3.1", "once_cell", - "parking_lot 0.12.3", + "parking_lot 0.12.4", "raw-window-handle", "scopeguard", "tao-macros", @@ -5224,7 +5358,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5255,9 +5389,9 @@ dependencies = [ [[package]] name = "tauri" -version = "2.6.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d" +checksum = "352a4bc7bf6c25f5624227e3641adf475a6535707451b09bb83271df8b7a6ac7" dependencies = [ "anyhow", "bytes", @@ -5282,7 +5416,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest 0.12.16", + "reqwest 0.12.22", "serde", "serde_json", "serde_repr", @@ -5306,9 +5440,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83" +checksum = "182d688496c06bf08ea896459bf483eb29cdff35c1c4c115fb14053514303064" dependencies = [ "anyhow", "cargo_toml", @@ -5316,21 +5450,21 @@ dependencies = [ "glob", "heck 0.5.0", "json-patch", - "schemars", + "schemars 0.8.22", "semver", "serde", "serde_json", "tauri-utils", "tauri-winres", - "toml", + "toml 0.8.23", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406" +checksum = "b54a99a6cd8e01abcfa61508177e6096a4fe2681efecee9214e962f2f073ae4a" dependencies = [ "base64 0.22.1", "brotli", @@ -5344,7 +5478,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "syn 2.0.101", + "syn 2.0.104", "tauri-utils", "thiserror 2.0.12", "time", @@ -5355,40 +5489,40 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc" +checksum = "7945b14dc45e23532f2ded6e120170bbdd4af5ceaa45784a6b33d250fbce3f9e" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3" +checksum = "5bd5c1e56990c70a906ef67a9851bbdba9136d26075ee9a2b19c8b46986b3e02" dependencies = [ "anyhow", "glob", "plist", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "tauri-utils", - "toml", + "toml 0.8.23", "walkdir", ] [[package]] name = "tauri-plugin-autostart" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58593aafcb03892dbf9998b35a96ead3b8e597435c7af46aff1654d076d5d03" +checksum = "062cdcd483d5e3148c9a64dabf8c574e239e2aa1193cf208d95cf89a676f87a5" dependencies = [ "auto-launch", "serde", @@ -5400,9 +5534,9 @@ dependencies = [ [[package]] name = "tauri-plugin-deep-link" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4976ac728ebc0487515aa956cfdf200abcc52b784e441493fc544bc6ce369c8" +checksum = "1fec67f32d7a06d80bd3dc009fdb678c35a66116d9cb8cd2bb32e406c2b5bbd2" dependencies = [ "dunce", "rust-ini", @@ -5420,9 +5554,9 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33318fe222fc2a612961de8b0419e2982767f213f54a4d3a21b0d7b85c41df8" +checksum = "05bedd4c3cf6f7aa97918a8814a736bd3695c9ddf3ede2d50eda6069c3290edc" dependencies = [ "log", "raw-window-handle", @@ -5438,15 +5572,15 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ead0daec5d305adcefe05af9d970fc437bcc7996052d564e7393eb291252da" +checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99" dependencies = [ "anyhow", "dunce", "glob", "percent-encoding", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "serde_repr", @@ -5454,7 +5588,7 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "url", ] @@ -5469,7 +5603,7 @@ dependencies = [ "objc2-app-kit", "objc2-foundation 0.3.1", "open", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "tauri", @@ -5482,9 +5616,9 @@ dependencies = [ [[package]] name = "tauri-plugin-os" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424f19432397850c2ddd42aa58078630c15287bbce3866eb1d90e7dbee680637" +checksum = "05bccb4c6de4299beec5a9b070878a01bce9e2c945aa7a75bcea38bcba4c675d" dependencies = [ "gethostname", "log", @@ -5500,16 +5634,16 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d" +checksum = "2b9ffadec5c3523f11e8273465cacb3d86ea7652a28e6e2a2e9b5c182f791d25" dependencies = [ "encoding_rs", "log", "open", "os_pipe", "regex", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "shared_child", @@ -5521,9 +5655,9 @@ dependencies = [ [[package]] name = "tauri-plugin-single-instance" -version = "2.2.4" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d0e07b40fb2eb13778e30778f5979347a2bf30e1b9d47f78ff7fe92d2e4b3d" +checksum = "50a0e5a4ce43cb3a733c3aef85e8478bc769dac743c615e26639cbf5d953faf7" dependencies = [ "serde", "serde_json", @@ -5531,15 +5665,15 @@ dependencies = [ "tauri-plugin-deep-link", "thiserror 2.0.12", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "zbus", ] [[package]] name = "tauri-runtime" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4" +checksum = "2b1cc885be806ea15ff7b0eb47098a7b16323d9228876afda329e34e2d6c4676" dependencies = [ "cookie", "dpi", @@ -5559,9 +5693,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad" +checksum = "fe653a2fbbef19fe898efc774bc52c8742576342a33d3d028c189b57eb1d2439" dependencies = [ "gtk", "http 1.3.1", @@ -5586,9 +5720,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e" +checksum = "9330c15cabfe1d9f213478c9e8ec2b0c76dab26bb6f314b8ad1c8a568c1d186e" dependencies = [ "anyhow", "brotli", @@ -5607,7 +5741,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "schemars", + "schemars 0.8.22", "semver", "serde", "serde-untagged", @@ -5615,7 +5749,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.12", - "toml", + "toml 0.8.23", "url", "urlpattern", "uuid", @@ -5629,8 +5763,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d321dbc6f998d825ab3f0d62673e810c861aac2d0de2cc2c395328f1d113b4" dependencies = [ "embed-resource", - "indexmap 2.9.0", - "toml", + "indexmap 2.10.0", + "toml 0.8.23", ] [[package]] @@ -5642,7 +5776,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -5683,7 +5817,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5694,7 +5828,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5786,17 +5920,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.5.10", "tokio-macros", "tracing", "windows-sys 0.52.0", @@ -5810,7 +5946,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5838,21 +5974,45 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.26", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.12", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] @@ -5863,8 +6023,8 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.9.0", - "toml_datetime", + "indexmap 2.10.0", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -5874,30 +6034,45 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.9.0", - "toml_datetime", + "indexmap 2.10.0", + "toml_datetime 0.6.11", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.10", + "winnow 0.7.12", +] + +[[package]] +name = "toml_parser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +dependencies = [ + "winnow 0.7.12", ] [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tower" @@ -5916,9 +6091,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5957,20 +6132,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -5997,12 +6172,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "try-lock" version = "0.2.5" @@ -6183,7 +6352,7 @@ checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ "getrandom 0.3.3", "js-sys", - "rand 0.9.1", + "rand 0.9.2", "serde", "uuid-macro-internal", "wasm-bindgen", @@ -6197,7 +6366,7 @@ checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6265,9 +6434,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -6306,7 +6475,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -6341,7 +6510,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6368,6 +6537,66 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.44", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +dependencies = [ + "bitflags 2.9.1", + "rustix 0.38.44", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +dependencies = [ + "bitflags 2.9.1", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml 0.37.5", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.77" @@ -6390,12 +6619,11 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5df295f8451142f1856b1bd86a606dfe9587d439bc036e319c827700dbd555e" +checksum = "aaf4f3c0ba838e82b4e5ccc4157003fb8c324ee24c058470ffb82820becbde98" dependencies = [ "core-foundation 0.10.1", - "home", "jni", "log", "ndk-context", @@ -6461,9 +6689,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -6490,7 +6718,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6510,7 +6738,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.12", + "redox_syscall 0.5.15", "wasite", "web-sys", ] @@ -6563,9 +6791,9 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core", @@ -6615,7 +6843,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6626,14 +6854,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-numerics" @@ -6647,9 +6875,9 @@ dependencies = [ [[package]] name = "windows-registry" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ "windows-link", "windows-result", @@ -6710,6 +6938,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -6749,13 +6986,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "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-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -6792,6 +7045,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6810,6 +7069,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6828,12 +7093,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[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_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6852,6 +7129,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6870,6 +7153,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6888,6 +7177,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6906,6 +7201,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -6917,9 +7218,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -6945,12 +7246,12 @@ dependencies = [ [[package]] name = "winreg" -version = "0.52.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -7071,12 +7372,12 @@ dependencies = [ [[package]] name = "xattr" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ "libc", - "rustix 1.0.7", + "rustix 1.0.8", ] [[package]] @@ -7114,15 +7415,15 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] [[package]] name = "zbus" -version = "5.7.1" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" dependencies = [ "async-broadcast", "async-executor", @@ -7146,7 +7447,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.7.10", + "winnow 0.7.12", "zbus_macros", "zbus_names", "zvariant", @@ -7154,14 +7455,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.7.1" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zbus_names", "zvariant", "zvariant_utils", @@ -7175,28 +7476,28 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.10", + "winnow 0.7.12", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -7216,7 +7517,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -7256,7 +7557,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -7289,29 +7590,29 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 0.7.10", + "winnow 0.7.12", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils", ] @@ -7325,6 +7626,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.101", - "winnow 0.7.10", + "syn 2.0.104", + "winnow 0.7.12", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fe8ae93..01f4852 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -25,7 +25,6 @@ tauri-build = { version = "2.0.0", features = [] } [dependencies] tauri-plugin-shell = "2.2.1" serde_json = "1" -rayon = "1.10.0" webbrowser = "1.0.2" url = "2.5.2" tauri-plugin-deep-link = "2" @@ -68,6 +67,11 @@ known-folders = "1.2.0" native_model = { version = "0.6.1", features = ["rmp_serde_1_3"] } tauri-plugin-opener = "2.4.0" bitcode = "0.6.6" +async-trait = "0.1.88" +futures = "0.3.31" +tokio-util = { version = "0.7.15", features = ["io"] } +async-scoped = { version = "0.9.0", features = ["use-tokio"] } +async-once-cell = "0.5.4" # tailscale = { path = "./tailscale" } [dependencies.dynfmt] @@ -94,14 +98,14 @@ features = ["fs"] version = "1.10.0" features = ["v4", "fast-rng", "macro-diagnostics"] -[dependencies.rustbreak] -version = "2" -features = ["other_errors"] # You can also use "yaml_enc" or "bin_enc" +[dependencies.dropbreak] +git = "https://github.com/Drop-OSS/dropbreak.git" +features = ["other_errors"] # You can also use "yaml_enc" or "bin_enc" [dependencies.reqwest] version = "0.12" default-features = false -features = ["json", "http2", "blocking", "rustls-tls-webpki-roots"] +features = ["json", "http2", "blocking", "rustls-tls-webpki-roots", "stream"] [dependencies.serde] version = "1" diff --git a/src-tauri/src/client/autostart.rs b/src-tauri/src/client/autostart.rs index a35f480..b7ec2e9 100644 --- a/src-tauri/src/client/autostart.rs +++ b/src-tauri/src/client/autostart.rs @@ -3,7 +3,7 @@ use log::debug; use tauri::AppHandle; use tauri_plugin_autostart::ManagerExt; -pub fn toggle_autostart_logic(app: AppHandle, enabled: bool) -> Result<(), String> { +pub async fn toggle_autostart_logic(app: AppHandle, enabled: bool) -> Result<(), String> { let manager = app.autolaunch(); if enabled { manager.enable().map_err(|e| e.to_string())?; @@ -14,16 +14,18 @@ pub fn toggle_autostart_logic(app: AppHandle, enabled: bool) -> Result<(), Strin } // Store the state in DB - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; db_handle.settings.autostart = enabled; drop(db_handle); Ok(()) } -pub fn get_autostart_enabled_logic(app: AppHandle) -> Result { +pub async fn get_autostart_enabled_logic( + app: AppHandle, +) -> Result { // First check DB state - let db_handle = borrow_db_checked(); + let db_handle = borrow_db_checked().await; let db_state = db_handle.settings.autostart; drop(db_handle); @@ -44,8 +46,8 @@ pub fn get_autostart_enabled_logic(app: AppHandle) -> Result Result<(), String> { - let db_handle = borrow_db_checked(); +pub async fn sync_autostart_on_startup(app: &AppHandle) -> Result<(), String> { + let db_handle = borrow_db_checked().await; let should_be_enabled = db_handle.settings.autostart; drop(db_handle); @@ -65,11 +67,11 @@ pub fn sync_autostart_on_startup(app: &AppHandle) -> Result<(), String> { Ok(()) } #[tauri::command] -pub fn toggle_autostart(app: AppHandle, enabled: bool) -> Result<(), String> { - toggle_autostart_logic(app, enabled) +pub async fn toggle_autostart(app: AppHandle, enabled: bool) -> Result<(), String> { + toggle_autostart_logic(app, enabled).await } #[tauri::command] -pub fn get_autostart_enabled(app: AppHandle) -> Result { - get_autostart_enabled_logic(app) +pub async fn get_autostart_enabled(app: AppHandle) -> Result { + get_autostart_enabled_logic(app).await } diff --git a/src-tauri/src/client/cleanup.rs b/src-tauri/src/client/cleanup.rs index f847b09..0cd0827 100644 --- a/src-tauri/src/client/cleanup.rs +++ b/src-tauri/src/client/cleanup.rs @@ -4,14 +4,19 @@ use tauri::AppHandle; use crate::AppState; #[tauri::command] -pub fn quit(app: tauri::AppHandle, state: tauri::State<'_, std::sync::Mutex>>) { - cleanup_and_exit(&app, &state); +pub async fn quit(app: tauri::AppHandle, state: tauri::State<'_, std::sync::Mutex>>) -> Result<(), ()> { + cleanup_and_exit(&app, &state).await; + + Ok(()) } -pub fn cleanup_and_exit(app: &AppHandle, state: &tauri::State<'_, std::sync::Mutex>>) { +pub async fn cleanup_and_exit( + app: &AppHandle, + state: &tauri::State<'_, std::sync::Mutex>>, +) { debug!("cleaning up and exiting application"); let download_manager = state.lock().unwrap().download_manager.clone(); - match download_manager.ensure_terminated() { + match download_manager.ensure_terminated().await { Ok(res) => match res { Ok(_) => debug!("download manager terminated correctly"), Err(_) => error!("download manager failed to terminate correctly"), diff --git a/src-tauri/src/database/commands.rs b/src-tauri/src/database/commands.rs index 494b568..28c0121 100644 --- a/src-tauri/src/database/commands.rs +++ b/src-tauri/src/database/commands.rs @@ -11,7 +11,7 @@ use crate::{ }; use super::{ - db::{borrow_db_checked, DATA_ROOT_DIR}, + db::{DATA_ROOT_DIR, borrow_db_checked}, debug::SystemData, models::data::Settings, }; @@ -19,19 +19,19 @@ use super::{ // Will, in future, return disk/remaining size // Just returns the directories that have been set up #[tauri::command] -pub fn fetch_download_dir_stats() -> Vec { - let lock = borrow_db_checked(); +pub async fn fetch_download_dir_stats() -> Vec { + let lock = borrow_db_checked().await; lock.applications.install_dirs.clone() } #[tauri::command] -pub fn delete_download_dir(index: usize) { - let mut lock = borrow_db_mut_checked(); +pub async fn delete_download_dir(index: usize) { + let mut lock = borrow_db_mut_checked().await; lock.applications.install_dirs.remove(index); } #[tauri::command] -pub fn add_download_dir(new_dir: PathBuf) -> Result<(), DownloadManagerError<()>> { +pub async fn add_download_dir(new_dir: PathBuf) -> Result<(), DownloadManagerError<()>> { // Check the new directory is all good let new_dir_path = Path::new(&new_dir); if new_dir_path.exists() { @@ -48,7 +48,7 @@ pub fn add_download_dir(new_dir: PathBuf) -> Result<(), DownloadManagerError<()> } // Add it to the dictionary - let mut lock = borrow_db_mut_checked(); + let mut lock = borrow_db_mut_checked().await; if lock.applications.install_dirs.contains(&new_dir) { return Err(Error::new( ErrorKind::AlreadyExists, @@ -63,8 +63,8 @@ pub fn add_download_dir(new_dir: PathBuf) -> Result<(), DownloadManagerError<()> } #[tauri::command] -pub fn update_settings(new_settings: Value) { - let mut db_lock = borrow_db_mut_checked(); +pub async fn update_settings(new_settings: Value) { + let mut db_lock = borrow_db_mut_checked().await; let mut current_settings = serde_json::to_value(db_lock.settings.clone()).unwrap(); for (key, value) in new_settings.as_object().unwrap() { current_settings[key] = value.clone(); @@ -73,12 +73,12 @@ pub fn update_settings(new_settings: Value) { db_lock.settings = new_settings; } #[tauri::command] -pub fn fetch_settings() -> Settings { - borrow_db_checked().settings.clone() +pub async fn fetch_settings() -> Settings { + borrow_db_checked().await.settings.clone() } #[tauri::command] -pub fn fetch_system_data() -> SystemData { - let db_handle = borrow_db_checked(); +pub async fn fetch_system_data() -> SystemData { + let db_handle = borrow_db_checked().await; SystemData::new( db_handle.auth.as_ref().unwrap().client_id.clone(), db_handle.base_url.clone(), diff --git a/src-tauri/src/database/db.rs b/src-tauri/src/database/db.rs index b4c5161..360b71b 100644 --- a/src-tauri/src/database/db.rs +++ b/src-tauri/src/database/db.rs @@ -3,14 +3,19 @@ use std::{ mem::ManuallyDrop, ops::{Deref, DerefMut}, path::PathBuf, - sync::{Arc, LazyLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{Arc, LazyLock}, }; +use async_once_cell::OnceCell; use chrono::Utc; -use log::{debug, error, info, warn}; +use dropbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError}; +use log::{debug, info, warn}; use native_model::{Decode, Encode}; -use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError}; -use serde::{de::DeserializeOwned, Serialize}; +use serde::{Serialize, de::DeserializeOwned}; +use tokio::{ + spawn, + sync::{RwLockReadGuard, RwLockWriteGuard}, +}; use url::Url; use crate::DB; @@ -27,15 +32,15 @@ pub struct DropDatabaseSerializer; impl DeSerializer for DropDatabaseSerializer { - fn serialize(&self, val: &T) -> rustbreak::error::DeSerResult> { + fn serialize(&self, val: &T) -> dropbreak::error::DeSerResult> { native_model::rmp_serde_1_3::RmpSerde::encode(val) .map_err(|e| DeSerError::Internal(e.to_string())) } - fn deserialize(&self, mut s: R) -> rustbreak::error::DeSerResult { + fn deserialize(&self, mut s: R) -> dropbreak::error::DeSerResult { let mut buf = Vec::new(); s.read_to_end(&mut buf) - .map_err(|e| rustbreak::error::DeSerError::Other(e.into()))?; + .map_err(|e| dropbreak::error::DeSerError::Other(e.into()))?; let val = native_model::rmp_serde_1_3::RmpSerde::decode(buf) .map_err(|e| DeSerError::Internal(e.to_string()))?; Ok(val) @@ -43,15 +48,33 @@ impl DeSerializer } pub type DatabaseInterface = - rustbreak::Database; + dropbreak::Database; + +pub struct OnceCellDatabase(OnceCell); +impl OnceCellDatabase { + pub const fn new() -> Self { + Self(OnceCell::new()) + } + + pub async fn init(&self, init: impl Future) { + self.0.get_or_init(init).await; + } +} +impl<'a> Deref for OnceCellDatabase { + type Target = DatabaseInterface; + + fn deref(&self) -> &Self::Target { + self.0.get().unwrap() + } +} pub trait DatabaseImpls { - fn set_up_database() -> DatabaseInterface; - fn database_is_set_up(&self) -> bool; - fn fetch_base_url(&self) -> Url; + async fn set_up_database() -> DatabaseInterface; + async fn database_is_set_up(&self) -> bool; + async fn fetch_base_url(&self) -> Url; } impl DatabaseImpls for DatabaseInterface { - fn set_up_database() -> DatabaseInterface { + async fn set_up_database() -> DatabaseInterface { let db_path = DATA_ROOT_DIR.join("drop.db"); let games_base_dir = DATA_ROOT_DIR.join("games"); let logs_root_dir = DATA_ROOT_DIR.join("logs"); @@ -68,9 +91,9 @@ impl DatabaseImpls for DatabaseInterface { let exists = fs::exists(db_path.clone()).unwrap(); match exists { - true => match PathDatabase::load_from_path(db_path.clone()) { + true => match PathDatabase::load_from_path(db_path.clone()).await { Ok(db) => db, - Err(e) => handle_invalid_database(e, db_path, games_base_dir, cache_dir), + Err(e) => handle_invalid_database(e, db_path, games_base_dir, cache_dir).await, }, false => { let default = Database::new(games_base_dir, None, cache_dir); @@ -79,28 +102,28 @@ impl DatabaseImpls for DatabaseInterface { db_path.as_os_str().to_str().unwrap() ); PathDatabase::create_at_path(db_path, default) + .await .expect("Database could not be created") } } } - fn database_is_set_up(&self) -> bool { - !self.borrow_data().unwrap().base_url.is_empty() + async fn database_is_set_up(&self) -> bool { + !self.borrow_data().await.base_url.is_empty() } - fn fetch_base_url(&self) -> Url { - let handle = self.borrow_data().unwrap(); + async fn fetch_base_url(&self) -> Url { + let handle = self.borrow_data().await; Url::parse(&handle.base_url).unwrap() } } -// TODO: Make the error relelvant rather than just assume that it's a Deserialize error -fn handle_invalid_database( +async fn handle_invalid_database( _e: RustbreakError, db_path: PathBuf, games_base_dir: PathBuf, cache_dir: PathBuf, -) -> rustbreak::Database { +) -> dropbreak::Database { warn!("{_e}"); let new_path = { let time = Utc::now().timestamp(); @@ -117,7 +140,9 @@ fn handle_invalid_database( cache_dir, ); - PathDatabase::create_at_path(db_path, db).expect("Database could not be created") + PathDatabase::create_at_path(db_path, db) + .await + .expect("Database could not be created") } // To automatically save the database upon drop @@ -148,31 +173,20 @@ impl<'a> Drop for DBWrite<'a> { ManuallyDrop::drop(&mut self.0); } - match DB.save() { - Ok(_) => {} - Err(e) => { - error!("database failed to save with error {e}"); - panic!("database failed to save with error {e}") + spawn(async { + match DB.save().await { + Ok(_) => {} + Err(e) => { + panic!("database failed to save with error {e}") + } } - } + }); } } -pub fn borrow_db_checked<'a>() -> DBRead<'a> { - match DB.borrow_data() { - Ok(data) => DBRead(data), - Err(e) => { - error!("database borrow failed with error {e}"); - panic!("database borrow failed with error {e}"); - } - } +pub async fn borrow_db_checked<'a>() -> DBRead<'a> { + DBRead(DB.borrow_data().await) } -pub fn borrow_db_mut_checked<'a>() -> DBWrite<'a> { - match DB.borrow_data_mut() { - Ok(data) => DBWrite(ManuallyDrop::new(data)), - Err(e) => { - error!("database borrow mut failed with error {e}"); - panic!("database borrow mut failed with error {e}"); - } - } +pub async fn borrow_db_mut_checked<'a>() -> DBWrite<'a> { + DBWrite(ManuallyDrop::new(DB.borrow_data_mut().await)) } diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index edc3061..aa2973f 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -1,4 +1,4 @@ pub mod commands; pub mod db; pub mod debug; -pub mod models; +pub mod models; \ No newline at end of file diff --git a/src-tauri/src/download_manager/download_manager_builder.rs b/src-tauri/src/download_manager/download_manager_builder.rs index c50ad20..2f915e5 100644 --- a/src-tauri/src/download_manager/download_manager_builder.rs +++ b/src-tauri/src/download_manager/download_manager_builder.rs @@ -1,14 +1,15 @@ use std::{ collections::HashMap, sync::{ - Arc, Mutex, + Arc, mpsc::{Receiver, Sender, channel}, }, - thread::{JoinHandle, spawn}, }; +use ::futures::future::join_all; use log::{debug, error, info, warn}; use tauri::{AppHandle, Emitter}; +use tokio::{spawn, sync::Mutex, task::JoinHandle}; use crate::{ database::models::data::DownloadableMetadata, @@ -69,7 +70,7 @@ Behold, my madness - quexeky pub struct DownloadManagerBuilder { download_agent_registry: HashMap, download_queue: Queue, - command_receiver: Receiver, + command_receiver: Mutex>, sender: Sender, progress: CurrentProgressObject, status: Arc>, @@ -89,7 +90,7 @@ impl DownloadManagerBuilder { let manager = Self { download_agent_registry: HashMap::new(), download_queue: queue.clone(), - command_receiver, + command_receiver: Mutex::new(command_receiver), status: status.clone(), sender: command_sender.clone(), progress: active_progress.clone(), @@ -100,86 +101,86 @@ impl DownloadManagerBuilder { active_control_flag: None, }; - let terminator = spawn(|| manager.manage_queue()); + let terminator = spawn(async{ manager.manage_queue().await }); DownloadManager::new(terminator, queue, active_progress, command_sender) } - fn set_status(&self, status: DownloadManagerStatus) { - *self.status.lock().unwrap() = status; + async fn set_status(&self, status: DownloadManagerStatus) { + *self.status.lock().await = status; } - fn remove_and_cleanup_front_download(&mut self, meta: &DownloadableMetadata) -> DownloadAgent { + async fn remove_and_cleanup_front_download(&mut self, meta: &DownloadableMetadata) -> DownloadAgent { self.download_queue.pop_front(); let download_agent = self.download_agent_registry.remove(meta).unwrap(); - self.cleanup_current_download(); + self.cleanup_current_download().await; download_agent } // CAREFUL WITH THIS FUNCTION // Make sure the download thread is terminated - fn cleanup_current_download(&mut self) { + async fn cleanup_current_download(&mut self) { self.active_control_flag = None; - *self.progress.lock().unwrap() = None; + *self.progress.lock().await = None; self.current_download_agent = None; - let mut download_thread_lock = self.current_download_thread.lock().unwrap(); + let mut download_thread_lock = self.current_download_thread.lock().await; *download_thread_lock = None; drop(download_thread_lock); } - fn stop_and_wait_current_download(&self) { - self.set_status(DownloadManagerStatus::Paused); + async fn stop_and_wait_current_download(&self) { + self.set_status(DownloadManagerStatus::Paused).await; if let Some(current_flag) = &self.active_control_flag { current_flag.set(DownloadThreadControlFlag::Stop); } - let mut download_thread_lock = self.current_download_thread.lock().unwrap(); + let mut download_thread_lock = self.current_download_thread.lock().await; if let Some(current_download_thread) = download_thread_lock.take() { - current_download_thread.join().unwrap(); + current_download_thread.await.unwrap(); } } - fn manage_queue(mut self) -> Result<(), ()> { + async fn manage_queue(mut self) -> Result<(), ()> { loop { - let signal = match self.command_receiver.recv() { + let signal = match self.command_receiver.lock().await.recv() { Ok(signal) => signal, Err(_) => return Err(()), }; match signal { DownloadManagerSignal::Go => { - self.manage_go_signal(); + self.manage_go_signal().await; } DownloadManagerSignal::Stop => { - self.manage_stop_signal(); + self.manage_stop_signal().await; } DownloadManagerSignal::Completed(meta) => { - self.manage_completed_signal(meta); + self.manage_completed_signal(meta).await; } DownloadManagerSignal::Queue(download_agent) => { - self.manage_queue_signal(download_agent); + self.manage_queue_signal(download_agent).await; } DownloadManagerSignal::Error(e) => { - self.manage_error_signal(e); + self.manage_error_signal(e).await; } DownloadManagerSignal::UpdateUIQueue => { - self.push_ui_queue_update(); + self.push_ui_queue_update().await; } DownloadManagerSignal::UpdateUIStats(kbs, time) => { self.push_ui_stats_update(kbs, time); } DownloadManagerSignal::Finish => { - self.stop_and_wait_current_download(); + self.stop_and_wait_current_download().await; return Ok(()); } DownloadManagerSignal::Cancel(meta) => { - self.manage_cancel_signal(&meta); + self.manage_cancel_signal(&meta).await; } }; } } - fn manage_queue_signal(&mut self, download_agent: DownloadAgent) { + async fn manage_queue_signal(&mut self, download_agent: DownloadAgent) { debug!("got signal Queue"); let meta = download_agent.metadata(); @@ -190,7 +191,7 @@ impl DownloadManagerBuilder { return; } - download_agent.on_initialised(&self.app_handle); + download_agent.on_initialised(&self.app_handle).await; self.download_queue.append(meta.clone()); self.download_agent_registry.insert(meta, download_agent); @@ -199,7 +200,7 @@ impl DownloadManagerBuilder { .unwrap(); } - fn manage_go_signal(&mut self) { + async fn manage_go_signal(&mut self) { debug!("got signal Go"); if self.download_agent_registry.is_empty() { debug!( @@ -233,28 +234,28 @@ impl DownloadManagerBuilder { .unwrap() .clone(); - self.active_control_flag = Some(download_agent.control_flag()); + self.active_control_flag = Some(download_agent.control_flag().await); self.current_download_agent = Some(download_agent.clone()); let sender = self.sender.clone(); - let mut download_thread_lock = self.current_download_thread.lock().unwrap(); + let mut download_thread_lock = self.current_download_thread.lock().await; let app_handle = self.app_handle.clone(); - *download_thread_lock = Some(spawn(move || { - match download_agent.download(&app_handle) { + *download_thread_lock = Some(spawn(async move { + match download_agent.download(&app_handle).await { // Ok(true) is for completed and exited properly Ok(true) => { debug!("download {:?} has completed", download_agent.metadata()); - match download_agent.validate() { + match download_agent.validate().await { Ok(true) => { - download_agent.on_complete(&app_handle); + download_agent.on_complete(&app_handle).await; sender .send(DownloadManagerSignal::Completed(download_agent.metadata())) .unwrap(); } Ok(false) => { - download_agent.on_incomplete(&app_handle); + download_agent.on_incomplete(&app_handle).await; } Err(e) => { error!( @@ -262,7 +263,7 @@ impl DownloadManagerBuilder { download_agent.metadata(), &e ); - download_agent.on_error(&app_handle, &e); + download_agent.on_error(&app_handle, &e).await; sender.send(DownloadManagerSignal::Error(e)).unwrap(); } } @@ -270,69 +271,69 @@ impl DownloadManagerBuilder { // Ok(false) is for incomplete but exited properly Ok(false) => { debug!("Donwload agent finished incomplete"); - download_agent.on_incomplete(&app_handle); + download_agent.on_incomplete(&app_handle).await; } Err(e) => { error!("download {:?} has error {}", download_agent.metadata(), &e); - download_agent.on_error(&app_handle, &e); + download_agent.on_error(&app_handle, &e).await; sender.send(DownloadManagerSignal::Error(e)).unwrap(); } } sender.send(DownloadManagerSignal::UpdateUIQueue).unwrap(); })); - self.set_status(DownloadManagerStatus::Downloading); + self.set_status(DownloadManagerStatus::Downloading).await; let active_control_flag = self.active_control_flag.clone().unwrap(); active_control_flag.set(DownloadThreadControlFlag::Go); } - fn manage_stop_signal(&mut self) { + async fn manage_stop_signal(&mut self) { debug!("got signal Stop"); if let Some(active_control_flag) = self.active_control_flag.clone() { - self.set_status(DownloadManagerStatus::Paused); + self.set_status(DownloadManagerStatus::Paused).await; active_control_flag.set(DownloadThreadControlFlag::Stop); } } - fn manage_completed_signal(&mut self, meta: DownloadableMetadata) { + async fn manage_completed_signal(&mut self, meta: DownloadableMetadata) { debug!("got signal Completed"); if let Some(interface) = &self.current_download_agent && interface.metadata() == meta { - self.remove_and_cleanup_front_download(&meta); + self.remove_and_cleanup_front_download(&meta).await; } - self.push_ui_queue_update(); + self.push_ui_queue_update().await; self.sender.send(DownloadManagerSignal::Go).unwrap(); } - fn manage_error_signal(&mut self, error: ApplicationDownloadError) { + async fn manage_error_signal(&mut self, error: ApplicationDownloadError) { debug!("got signal Error"); if let Some(current_agent) = self.current_download_agent.clone() { - current_agent.on_error(&self.app_handle, &error); + current_agent.on_error(&self.app_handle, &error).await; - self.stop_and_wait_current_download(); - self.remove_and_cleanup_front_download(¤t_agent.metadata()); + self.stop_and_wait_current_download().await; + self.remove_and_cleanup_front_download(¤t_agent.metadata()).await; } - self.set_status(DownloadManagerStatus::Error); + self.set_status(DownloadManagerStatus::Error).await; } - fn manage_cancel_signal(&mut self, meta: &DownloadableMetadata) { + async fn manage_cancel_signal(&mut self, meta: &DownloadableMetadata) { debug!("got signal Cancel"); if let Some(current_download) = &self.current_download_agent { if ¤t_download.metadata() == meta { - self.set_status(DownloadManagerStatus::Paused); - current_download.on_cancelled(&self.app_handle); - self.stop_and_wait_current_download(); + self.set_status(DownloadManagerStatus::Paused).await; + current_download.on_cancelled(&self.app_handle).await; + self.stop_and_wait_current_download().await; self.download_queue.pop_front(); - self.cleanup_current_download(); + self.cleanup_current_download().await; debug!("current download queue: {:?}", self.download_queue.read()); } // TODO: Collapse these two into a single if statement somehow else if let Some(download_agent) = self.download_agent_registry.get(meta) { let index = self.download_queue.get_by_meta(meta); if let Some(index) = index { - download_agent.on_cancelled(&self.app_handle); + download_agent.on_cancelled(&self.app_handle).await; let _ = self.download_queue.edit().remove(index).unwrap(); let removed = self.download_agent_registry.remove(meta); debug!( @@ -345,7 +346,7 @@ impl DownloadManagerBuilder { } else if let Some(download_agent) = self.download_agent_registry.get(meta) { let index = self.download_queue.get_by_meta(meta); if let Some(index) = index { - download_agent.on_cancelled(&self.app_handle); + download_agent.on_cancelled(&self.app_handle).await; let _ = self.download_queue.edit().remove(index).unwrap(); let removed = self.download_agent_registry.remove(meta); debug!( @@ -355,28 +356,27 @@ impl DownloadManagerBuilder { ); } } - self.push_ui_queue_update(); + self.push_ui_queue_update().await; } fn push_ui_stats_update(&self, kbs: usize, time: usize) { let event_data = StatsUpdateEvent { speed: kbs, time }; self.app_handle.emit("update_stats", event_data).unwrap(); } - fn push_ui_queue_update(&self) { + async fn push_ui_queue_update(&self) { let queue = &self.download_queue.read(); - let queue_objs = queue + let queue_objs = join_all(queue .iter() - .map(|key| { + .map(async |key| { let val = self.download_agent_registry.get(key).unwrap(); QueueUpdateEventQueueData { meta: DownloadableMetadata::clone(key), - status: val.status(), - progress: val.progress().get_progress(), - current: val.progress().sum(), - max: val.progress().get_max(), + status: val.status().await, + progress: val.progress().await.get_progress(), + current: val.progress().await.sum(), + max: val.progress().await.get_max(), } - }) - .collect(); + })).await; let event_data = QueueUpdateEvent { queue: queue_objs }; self.app_handle.emit("update_queue", event_data).unwrap(); diff --git a/src-tauri/src/download_manager/download_manager_frontend.rs b/src-tauri/src/download_manager/download_manager_frontend.rs index 359903d..3783d99 100644 --- a/src-tauri/src/download_manager/download_manager_frontend.rs +++ b/src-tauri/src/download_manager/download_manager_frontend.rs @@ -1,16 +1,15 @@ use std::{ - any::Any, collections::VecDeque, fmt::Debug, sync::{ mpsc::{SendError, Sender}, Mutex, MutexGuard, }, - thread::JoinHandle, }; use log::{debug, info}; use serde::Serialize; +use tokio::task::{JoinError, JoinHandle}; use crate::{ database::models::data::DownloadableMetadata, @@ -118,8 +117,8 @@ impl DownloadManager { pub fn read_queue(&self) -> VecDeque { self.download_queue.read() } - pub fn get_current_download_progress(&self) -> Option { - let progress_object = (*self.progress.lock().unwrap()).clone()?; + pub async fn get_current_download_progress(&self) -> Option { + let progress_object = (*self.progress.lock().await).clone()?; Some(progress_object.get_progress()) } pub fn rearrange_string(&self, meta: &DownloadableMetadata, new_index: usize) { @@ -171,12 +170,12 @@ impl DownloadManager { pub fn resume_downloads(&self) { self.command_sender.send(DownloadManagerSignal::Go).unwrap(); } - pub fn ensure_terminated(&self) -> Result, Box> { + pub async fn ensure_terminated(&self) -> Result, JoinError> { self.command_sender .send(DownloadManagerSignal::Finish) .unwrap(); let terminator = self.terminator.lock().unwrap().take(); - terminator.unwrap().join() + terminator.unwrap().await } pub fn get_sender(&self) -> Sender { self.command_sender.clone() diff --git a/src-tauri/src/download_manager/downloadable.rs b/src-tauri/src/download_manager/downloadable.rs index fff8011..4e37293 100644 --- a/src-tauri/src/download_manager/downloadable.rs +++ b/src-tauri/src/download_manager/downloadable.rs @@ -12,16 +12,17 @@ use super::{ util::{download_thread_control_flag::DownloadThreadControl, progress_object::ProgressObject}, }; +#[async_trait::async_trait] pub trait Downloadable: Send + Sync { - fn download(&self, app_handle: &AppHandle) -> Result; - fn progress(&self) -> Arc; - fn control_flag(&self) -> DownloadThreadControl; - fn validate(&self) -> Result; - fn status(&self) -> DownloadStatus; + async fn download(&self, app_handle: &AppHandle) -> Result; + async fn progress(&self) -> Arc; + async fn control_flag(&self) -> DownloadThreadControl; + async fn validate(&self) -> Result; + async fn status(&self) -> DownloadStatus; fn metadata(&self) -> DownloadableMetadata; - fn on_initialised(&self, app_handle: &AppHandle); - fn on_error(&self, app_handle: &AppHandle, error: &ApplicationDownloadError); - fn on_complete(&self, app_handle: &AppHandle); - fn on_incomplete(&self, app_handle: &AppHandle); - fn on_cancelled(&self, app_handle: &AppHandle); + async fn on_initialised(&self, app_handle: &AppHandle); + async fn on_error(&self, app_handle: &AppHandle, error: &ApplicationDownloadError); + async fn on_complete(&self, app_handle: &AppHandle); + async fn on_incomplete(&self, app_handle: &AppHandle); + async fn on_cancelled(&self, app_handle: &AppHandle); } diff --git a/src-tauri/src/games/collections/commands.rs b/src-tauri/src/games/collections/commands.rs index 7f81528..6250d48 100644 --- a/src-tauri/src/games/collections/commands.rs +++ b/src-tauri/src/games/collections/commands.rs @@ -1,110 +1,116 @@ -use reqwest::blocking::Client; +use reqwest::Client; use serde_json::json; use url::Url; use crate::{ + DB, database::db::DatabaseImpls, error::remote_access_error::RemoteAccessError, remote::{auth::generate_authorization_header, requests::make_request}, - DB, }; use super::collection::{Collection, Collections}; #[tauri::command] -pub fn fetch_collections() -> Result { +pub async fn fetch_collections() -> Result { let client = Client::new(); - let response = make_request(&client, &["/api/v1/client/collection"], &[], |r| { - r.header("Authorization", generate_authorization_header()) - })? - .send()?; + let response = make_request(&client, &["/api/v1/client/collection"], &[], async |r| { + r.header("Authorization", generate_authorization_header().await) + }) + .await? + .send() + .await?; - Ok(response.json()?) + Ok(response.json().await?) } #[tauri::command] -pub fn fetch_collection(collection_id: String) -> Result { +pub async fn fetch_collection(collection_id: String) -> Result { let client = Client::new(); let response = make_request( &client, &["/api/v1/client/collection/", &collection_id], &[], - |r| r.header("Authorization", generate_authorization_header()), - )? - .send()?; + async |r| r.header("Authorization", generate_authorization_header().await), + ) + .await? + .send() + .await?; - Ok(response.json()?) + Ok(response.json().await?) } #[tauri::command] -pub fn create_collection(name: String) -> Result { +pub async fn create_collection(name: String) -> Result { let client = Client::new(); - let base_url = DB.fetch_base_url(); + let base_url = DB.fetch_base_url().await; let base_url = Url::parse(&format!("{base_url}api/v1/client/collection/"))?; let response = client .post(base_url) - .header("Authorization", generate_authorization_header()) + .header("Authorization", generate_authorization_header().await) .json(&json!({"name": name})) - .send()?; + .send() + .await?; - Ok(response.json()?) + Ok(response.json().await?) } #[tauri::command] -pub fn add_game_to_collection( +pub async fn add_game_to_collection( collection_id: String, game_id: String, ) -> Result<(), RemoteAccessError> { let client = Client::new(); let url = Url::parse(&format!( "{}api/v1/client/collection/{}/entry/", - DB.fetch_base_url(), + DB.fetch_base_url().await, collection_id ))?; client .post(url) - .header("Authorization", generate_authorization_header()) + .header("Authorization", generate_authorization_header().await) .json(&json!({"id": game_id})) - .send()?; + .send() + .await?; Ok(()) } #[tauri::command] -pub fn delete_collection(collection_id: String) -> Result { +pub async fn delete_collection(collection_id: String) -> Result<(), RemoteAccessError> { let client = Client::new(); let base_url = Url::parse(&format!( "{}api/v1/client/collection/{}", - DB.fetch_base_url(), + DB.fetch_base_url().await, collection_id ))?; - let response = client + client .delete(base_url) - .header("Authorization", generate_authorization_header()) - .send()?; + .header("Authorization", generate_authorization_header().await) + .send().await?; - Ok(response.json()?) + Ok(()) } #[tauri::command] -pub fn delete_game_in_collection( +pub async fn delete_game_in_collection( collection_id: String, game_id: String, ) -> Result<(), RemoteAccessError> { let client = Client::new(); let base_url = Url::parse(&format!( "{}api/v1/client/collection/{}/entry", - DB.fetch_base_url(), + DB.fetch_base_url().await, collection_id ))?; client .delete(base_url) - .header("Authorization", generate_authorization_header()) + .header("Authorization", generate_authorization_header().await) .json(&json!({"id": game_id})) - .send()?; + .send().await?; Ok(()) } diff --git a/src-tauri/src/games/commands.rs b/src-tauri/src/games/commands.rs index 8a95132..d19c1e6 100644 --- a/src-tauri/src/games/commands.rs +++ b/src-tauri/src/games/commands.rs @@ -1,28 +1,29 @@ -use std::sync::Mutex; - +use serde::Deserialize; use tauri::AppHandle; +use tokio::sync::Mutex; use crate::{ - database::models::data::GameVersion, + AppState, + database::{db::borrow_db_mut_checked, models::data::GameVersion}, error::{library_error::LibraryError, remote_access_error::RemoteAccessError}, games::library::{ fetch_game_logic_offline, fetch_library_logic_offline, get_current_meta, uninstall_game_logic, }, - offline, AppState, + offline, }; use super::{ library::{ - fetch_game_logic, fetch_game_verion_options_logic, fetch_library_logic, FetchGameStruct, - Game, + FetchGameStruct, Game, fetch_game_logic, fetch_game_verion_options_logic, + fetch_library_logic, }, state::{GameStatusManager, GameStatusWithTransient}, }; #[tauri::command] -pub fn fetch_library( - state: tauri::State<'_, Mutex>, +pub async fn fetch_library( + state: tauri::State<'_, Mutex>>, ) -> Result, RemoteAccessError> { offline!( state, @@ -30,12 +31,13 @@ pub fn fetch_library( fetch_library_logic_offline, state ) + .await } #[tauri::command] -pub fn fetch_game( +pub async fn fetch_game( game_id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result { offline!( state, @@ -44,28 +46,74 @@ pub fn fetch_game( game_id, state ) + .await } #[tauri::command] -pub fn fetch_game_status(id: String) -> GameStatusWithTransient { - GameStatusManager::fetch_state(&id) +pub async fn fetch_game_status(id: String) -> GameStatusWithTransient { + GameStatusManager::fetch_state(&id).await } #[tauri::command] -pub fn uninstall_game(game_id: String, app_handle: AppHandle) -> Result<(), LibraryError> { - let meta = match get_current_meta(&game_id) { +pub async fn uninstall_game(game_id: String, app_handle: AppHandle) -> Result<(), LibraryError> { + let meta = match get_current_meta(&game_id).await { Some(data) => data, None => return Err(LibraryError::MetaNotFound(game_id)), }; - uninstall_game_logic(meta, &app_handle); + uninstall_game_logic(meta, &app_handle).await; Ok(()) } #[tauri::command] -pub fn fetch_game_verion_options( +pub async fn fetch_game_verion_options( game_id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result, RemoteAccessError> { - fetch_game_verion_options_logic(game_id, state) + fetch_game_verion_options_logic(game_id, state).await +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FrontendGameOptions { + launch_string: String, +} + +#[tauri::command] +pub async fn update_game_configuration( + game_id: String, + options: FrontendGameOptions, +) -> Result<(), LibraryError> { + let mut handle = borrow_db_mut_checked().await; + let installed_version = handle + .applications + .installed_game_version + .get(&game_id) + .ok_or(LibraryError::MetaNotFound(game_id))?; + + let id = installed_version.id.clone(); + let version = installed_version.version.clone().unwrap(); + + let mut existing_configuration = handle + .applications + .game_versions + .get(&id) + .unwrap() + .get(&version) + .unwrap() + .clone(); + + // Add more options in here + existing_configuration.launch_command_template = options.launch_string; + + // Add no more options past here + + handle + .applications + .game_versions + .get_mut(&id) + .unwrap() + .insert(version.to_string(), existing_configuration); + + Ok(()) } diff --git a/src-tauri/src/games/downloads/commands.rs b/src-tauri/src/games/downloads/commands.rs index 7e48162..2115ab0 100644 --- a/src-tauri/src/games/downloads/commands.rs +++ b/src-tauri/src/games/downloads/commands.rs @@ -15,11 +15,11 @@ use crate::{ use super::download_agent::GameDownloadAgent; #[tauri::command] -pub fn download_game( +pub async fn download_game( game_id: String, game_version: String, install_dir: usize, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result<(), DownloadManagerError> { let sender = state.lock().unwrap().download_manager.get_sender(); let game_download_agent = Arc::new(Box::new(GameDownloadAgent::new_from_index( @@ -27,7 +27,7 @@ pub fn download_game( game_version, install_dir, sender, - )) as Box); + ).await) as Box); Ok(state .lock() .unwrap() @@ -36,11 +36,11 @@ pub fn download_game( } #[tauri::command] -pub fn resume_download( +pub async fn resume_download( game_id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result<(), DownloadManagerError> { - let s = borrow_db_checked() + let s = borrow_db_checked().await .applications .game_statuses .get(&game_id) diff --git a/src-tauri/src/games/downloads/download_agent.rs b/src-tauri/src/games/downloads/download_agent.rs index 7a452cb..4d5fa80 100644 --- a/src-tauri/src/games/downloads/download_agent.rs +++ b/src-tauri/src/games/downloads/download_agent.rs @@ -15,22 +15,28 @@ use crate::games::downloads::manifest::{DropDownloadContext, DropManifest}; use crate::games::downloads::validate::game_validate_logic; use crate::games::library::{on_game_complete, on_game_incomplete, push_game_update}; use crate::remote::requests::make_request; -use log::{debug, error, info}; -use rayon::ThreadPoolBuilder; +use log::{debug, error, info, warn}; use std::collections::HashMap; -use std::fs::{create_dir_all, OpenOptions}; +use std::fs::{OpenOptions, create_dir_all}; use std::path::{Path, PathBuf}; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::time::Instant; use tauri::{AppHandle, Emitter}; +use tokio::sync::mpsc; #[cfg(target_os = "linux")] -use rustix::fs::{fallocate, FallocateFlags}; +use rustix::fs::{FallocateFlags, fallocate}; use super::download_logic::download_game_chunk; use super::drop_data::DropData; +// This is cursed but necessary +// See the message where it is used +unsafe fn extend_lifetime<'b, R>(r: &'b R) -> &'static R { + unsafe { std::mem::transmute::<&'b R, &'static R>(r) } +} + pub struct GameDownloadAgent { pub id: String, pub version: String, @@ -45,13 +51,13 @@ pub struct GameDownloadAgent { } impl GameDownloadAgent { - pub fn new_from_index( + pub async fn new_from_index( id: String, version: String, target_download_dir: usize, sender: Sender, ) -> Self { - let db_lock = borrow_db_checked(); + let db_lock = borrow_db_checked().await; let base_dir = db_lock.applications.install_dirs[target_download_dir].clone(); drop(db_lock); @@ -87,8 +93,8 @@ impl GameDownloadAgent { } // Blocking - pub fn setup_download(&self) -> Result<(), ApplicationDownloadError> { - self.ensure_manifest_exists()?; + pub async fn setup_download(&self) -> Result<(), ApplicationDownloadError> { + self.ensure_manifest_exists().await?; self.ensure_contexts()?; @@ -98,8 +104,8 @@ impl GameDownloadAgent { } // Blocking - pub fn download(&self, app_handle: &AppHandle) -> Result { - self.setup_download()?; + pub async fn download(&self, app_handle: &AppHandle) -> Result { + self.setup_download().await?; self.set_progress_object_params(); let timer = Instant::now(); push_game_update( @@ -115,6 +121,7 @@ impl GameDownloadAgent { ); let res = self .run() + .await .map_err(|_| ApplicationDownloadError::DownloadError); debug!( @@ -125,37 +132,39 @@ impl GameDownloadAgent { res } - pub fn ensure_manifest_exists(&self) -> Result<(), ApplicationDownloadError> { + pub async fn ensure_manifest_exists(&self) -> Result<(), ApplicationDownloadError> { if self.manifest.lock().unwrap().is_some() { return Ok(()); } - self.download_manifest() + self.download_manifest().await } - fn download_manifest(&self) -> Result<(), ApplicationDownloadError> { - let header = generate_authorization_header(); - let client = reqwest::blocking::Client::new(); + async fn download_manifest(&self) -> Result<(), ApplicationDownloadError> { + let header = generate_authorization_header().await; + let client = reqwest::Client::new(); let response = make_request( &client, &["/api/v1/client/game/manifest"], &[("id", &self.id), ("version", &self.version)], - |f| f.header("Authorization", header), + async |f| f.header("Authorization", header), ) + .await .map_err(ApplicationDownloadError::Communication)? .send() + .await .map_err(|e| ApplicationDownloadError::Communication(e.into()))?; if response.status() != 200 { return Err(ApplicationDownloadError::Communication( RemoteAccessError::ManifestDownloadFailed( response.status(), - response.text().unwrap(), + response.text().await.unwrap(), ), )); } - let manifest_download: DropManifest = response.json().unwrap(); + let manifest_download: DropManifest = response.json().await.unwrap(); if let Ok(mut manifest) = self.manifest.lock() { *manifest = Some(manifest_download); @@ -251,30 +260,35 @@ impl GameDownloadAgent { Ok(()) } - // TODO: Change return value on Err - pub fn run(&self) -> Result { - let max_download_threads = borrow_db_checked().settings.max_download_threads; + pub async fn run(&self) -> Result { + let max_download_threads = borrow_db_checked().await.settings.max_download_threads; debug!( "downloading game: {} with {} threads", self.id, max_download_threads ); - let pool = ThreadPoolBuilder::new() - .num_threads(max_download_threads) + + let client = &reqwest::Client::new(); + + let rt = tokio::runtime::Builder::new_multi_thread() + .worker_threads(max_download_threads) + .thread_name("drop-download-thread") .build() - .unwrap(); + .map_err(|e| { + warn!("failed to create download scheduler: {e}"); + () + })?; - let completed_contexts = Arc::new(boxcar::Vec::new()); - let completed_indexes_loop_arc = completed_contexts.clone(); + let (tx, mut rx) = mpsc::channel(32); + + // Scope this for safety + { + let contexts = self.contexts.lock().unwrap(); + debug!("{contexts:#?}"); - let contexts = self.contexts.lock().unwrap(); - debug!("{contexts:#?}"); - pool.scope(|scope| { - let client = &reqwest::blocking::Client::new(); let context_map = self.context_map.lock().unwrap(); for (index, context) in contexts.iter().enumerate() { let client = client.clone(); - let completed_indexes = completed_indexes_loop_arc.clone(); let progress = self.progress.get(index); let progress_handle = ProgressHandle::new(progress, self.progress.clone()); @@ -287,33 +301,48 @@ impl GameDownloadAgent { let sender = self.sender.clone(); - let request = match make_request( - &client, - &["/api/v1/client/chunk"], - &[ - ("id", &context.game_id), - ("version", &context.version), - ("name", &context.file_name), - ("chunk", &context.index.to_string()), - ], - |r| r, - ) { - Ok(request) => request, - Err(e) => { - sender - .send(DownloadManagerSignal::Error( - ApplicationDownloadError::Communication(e), - )) - .unwrap(); - continue; - } - }; + let local_tx = tx.clone(); + /* + This lifetime extensions are necessary, because this loop acts like a scope + but Rust doesn't know that. + */ + let context = unsafe { extend_lifetime(context) }; + let self_static = unsafe { extend_lifetime(self) }; + rt.spawn(async move { + let request = match make_request( + &client, + &["/api/v1/client/chunk"], + &[ + ("id", &context.game_id), + ("version", &context.version), + ("name", &context.file_name), + ("chunk", &context.index.to_string()), + ], + async |r| r, + ) + .await + { + Ok(request) => request, + Err(e) => { + sender + .send(DownloadManagerSignal::Error( + ApplicationDownloadError::Communication(e), + )) + .unwrap(); + return; + } + }; - scope.spawn(move |_| { - match download_game_chunk(context, &self.control_flag, progress_handle, request) + match download_game_chunk( + context, + &self_static.control_flag, + progress_handle, + request, + ) + .await { Ok(true) => { - completed_indexes.push(context.checksum.clone()); + local_tx.send(context.checksum.clone()).await.unwrap(); } Ok(false) => {} Err(e) => { @@ -323,19 +352,22 @@ impl GameDownloadAgent { } }); } - }); + } - let newly_completed = completed_contexts.to_owned(); + let mut newly_completed = Vec::new(); + while let Some(completed_checksum) = rx.recv().await { + println!("completed checksum {}", completed_checksum); + newly_completed.push(completed_checksum); + } - let completed_lock_len = { - let mut context_map_lock = self.context_map.lock().unwrap(); - for (_, item) in newly_completed.iter() { - context_map_lock.insert(item.clone(), true); - } + // 'return' from the download + let mut context_map_lock = self.context_map.lock().unwrap(); + for item in newly_completed.iter() { + context_map_lock.insert(item.clone(), true); + } + let completed_lock_len = context_map_lock.values().filter(|x| **x).count(); - context_map_lock.values().filter(|x| **x).count() - }; - let context_map_lock = self.context_map.lock().unwrap(); + let contexts = self.contexts.lock().unwrap(); let contexts = contexts .iter() .map(|x| { @@ -345,6 +377,7 @@ impl GameDownloadAgent { ) }) .collect::>(); + drop(context_map_lock); self.stored_manifest.set_contexts(&contexts); @@ -365,20 +398,8 @@ impl GameDownloadAgent { } } +#[async_trait::async_trait] impl Downloadable for GameDownloadAgent { - fn download(&self, app_handle: &AppHandle) -> Result { - *self.status.lock().unwrap() = DownloadStatus::Downloading; - self.download(app_handle) - } - - fn progress(&self) -> Arc { - self.progress.clone() - } - - fn control_flag(&self) -> DownloadThreadControl { - self.control_flag.clone() - } - fn metadata(&self) -> DownloadableMetadata { DownloadableMetadata { id: self.id.clone(), @@ -387,11 +408,24 @@ impl Downloadable for GameDownloadAgent { } } - fn on_initialised(&self, _app_handle: &tauri::AppHandle) { + async fn download(&self, app_handle: &AppHandle) -> Result { + *self.status.lock().unwrap() = DownloadStatus::Downloading; + self.download(app_handle).await + } + + async fn progress(&self) -> Arc { + self.progress.clone() + } + + async fn control_flag(&self) -> DownloadThreadControl { + self.control_flag.clone() + } + + async fn on_initialised(&self, _app_handle: &tauri::AppHandle) { *self.status.lock().unwrap() = DownloadStatus::Queued; } - fn on_error(&self, app_handle: &tauri::AppHandle, error: &ApplicationDownloadError) { + async fn on_error(&self, app_handle: &tauri::AppHandle, error: &ApplicationDownloadError) { *self.status.lock().unwrap() = DownloadStatus::Error; app_handle .emit("download_error", error.to_string()) @@ -399,46 +433,49 @@ impl Downloadable for GameDownloadAgent { error!("error while managing download: {error}"); - let mut handle = borrow_db_mut_checked(); + let mut handle = borrow_db_mut_checked().await; handle .applications .transient_statuses .remove(&self.metadata()); } - fn on_complete(&self, app_handle: &tauri::AppHandle) { + async fn on_complete(&self, app_handle: &tauri::AppHandle) { on_game_complete( &self.metadata(), self.stored_manifest.base_path.to_string_lossy().to_string(), app_handle, ) + .await .unwrap(); } - // TODO: fix this function. It doesn't restart the download properly, nor does it reset the state properly - fn on_incomplete(&self, app_handle: &tauri::AppHandle) { + async fn on_incomplete(&self, app_handle: &tauri::AppHandle) { on_game_incomplete( &self.metadata(), self.stored_manifest.base_path.to_string_lossy().to_string(), app_handle, ) + .await .unwrap(); } - fn on_cancelled(&self, _app_handle: &tauri::AppHandle) {} + async fn on_cancelled(&self, _app_handle: &tauri::AppHandle) {} - fn status(&self) -> DownloadStatus { + async fn status(&self) -> DownloadStatus { self.status.lock().unwrap().clone() } - fn validate(&self) -> Result { + async fn validate(&self) -> Result { *self.status.lock().unwrap() = DownloadStatus::Validating; + let contexts = self.contexts.lock().unwrap().clone(); game_validate_logic( &self.stored_manifest, - self.contexts.lock().unwrap().clone(), + contexts, self.progress.clone(), self.sender.clone(), &self.control_flag, ) + .await } } diff --git a/src-tauri/src/games/downloads/download_logic.rs b/src-tauri/src/games/downloads/download_logic.rs index 5985a62..c870ce4 100644 --- a/src-tauri/src/games/downloads/download_logic.rs +++ b/src-tauri/src/games/downloads/download_logic.rs @@ -7,68 +7,70 @@ use crate::error::drop_server_error::DropServerError; use crate::error::remote_access_error::RemoteAccessError; use crate::games::downloads::manifest::DropDownloadContext; use crate::remote::auth::generate_authorization_header; +use futures::TryStreamExt; use log::{debug, warn}; use md5::{Context, Digest}; -use reqwest::blocking::{RequestBuilder, Response}; +use reqwest::RequestBuilder; +use tokio::fs::{File, OpenOptions}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt, AsyncWrite, AsyncWriteExt}; +use tokio_util::io::StreamReader; -use std::fs::{set_permissions, Permissions}; -use std::io::Read; +use std::fs::{Permissions, set_permissions}; +use std::io::Write; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use std::{ - fs::{File, OpenOptions}, - io::{self, BufWriter, Seek, SeekFrom, Write}, + io::{self, SeekFrom}, path::PathBuf, }; -pub struct DropWriter { +pub struct DropWriter { hasher: Context, destination: W, } impl DropWriter { - fn new(path: PathBuf) -> Self { + async fn new(path: PathBuf) -> Self { Self { - destination: OpenOptions::new().write(true).open(path).unwrap(), + destination: OpenOptions::new().write(true).open(path).await.unwrap(), hasher: Context::new(), } } - fn finish(mut self) -> io::Result { - self.flush().unwrap(); + async fn finish(mut self) -> io::Result { + self.flush().await.unwrap(); Ok(self.hasher.compute()) } -} -// Write automatically pushes to file and hasher -impl Write for DropWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { + + async fn write(&mut self, mut buf: &[u8]) -> io::Result<()> { self.hasher .write_all(buf) .map_err(|e| io::Error::other(format!("Unable to write to hasher: {e}")))?; - self.destination.write(buf) + self.destination.write_all_buf(&mut buf).await } - fn flush(&mut self) -> io::Result<()> { + async fn flush(&mut self) -> io::Result<()> { self.hasher.flush()?; - self.destination.flush() + self.destination.flush().await } -} -// Seek moves around destination output -impl Seek for DropWriter { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.destination.seek(pos) + + async fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.destination.seek(pos).await } } -pub struct DropDownloadPipeline<'a, R: Read, W: Write> { +pub struct DropDownloadPipeline<'a, R: AsyncRead, W: AsyncWrite> { pub source: R, pub destination: DropWriter, pub control_flag: &'a DownloadThreadControl, pub progress: ProgressHandle, pub size: usize, } -impl<'a> DropDownloadPipeline<'a, Response, File> { +impl<'a, R> DropDownloadPipeline<'a, R, File> +where + R: AsyncRead + Unpin, +{ fn new( - source: Response, + source: R, destination: DropWriter, control_flag: &'a DownloadThreadControl, progress: ProgressHandle, @@ -83,19 +85,18 @@ impl<'a> DropDownloadPipeline<'a, Response, File> { } } - fn copy(&mut self) -> Result { + async fn copy(&mut self) -> Result { let copy_buf_size = 512; let mut copy_buf = vec![0; copy_buf_size]; - let mut buf_writer = BufWriter::with_capacity(1024 * 1024, &mut self.destination); let mut current_size = 0; loop { if self.control_flag.get() == DownloadThreadControlFlag::Stop { - buf_writer.flush()?; + self.destination.flush().await?; return Ok(false); } - let mut bytes_read = self.source.read(&mut copy_buf)?; + let mut bytes_read = self.source.read(&mut copy_buf).await?; current_size += bytes_read; if current_size > self.size { @@ -105,7 +106,7 @@ impl<'a> DropDownloadPipeline<'a, Response, File> { current_size = self.size; } - buf_writer.write_all(©_buf[0..bytes_read])?; + self.destination.write(©_buf[0..bytes_read]).await?; self.progress.add(bytes_read); if current_size >= self.size { @@ -116,18 +117,18 @@ impl<'a> DropDownloadPipeline<'a, Response, File> { break; } } - buf_writer.flush()?; + self.destination.flush().await?; Ok(true) } - fn finish(self) -> Result { - let checksum = self.destination.finish()?; + async fn finish(self) -> Result { + let checksum = self.destination.finish().await?; Ok(checksum) } } -pub fn download_game_chunk( +pub async fn download_game_chunk( ctx: &DropDownloadContext, control_flag: &DownloadThreadControl, progress: ProgressHandle, @@ -139,13 +140,14 @@ pub fn download_game_chunk( return Ok(false); } let response = request - .header("Authorization", generate_authorization_header()) + .header("Authorization", generate_authorization_header().await) .send() + .await .map_err(|e| ApplicationDownloadError::Communication(e.into()))?; if response.status() != 200 { debug!("chunk request got status code: {}", response.status()); - let raw_res = response.text().unwrap(); + let raw_res = response.text().await.unwrap(); if let Ok(err) = serde_json::from_str::(&raw_res) { return Err(ApplicationDownloadError::Communication( RemoteAccessError::InvalidResponse(err), @@ -156,11 +158,12 @@ pub fn download_game_chunk( )); } - let mut destination = DropWriter::new(ctx.path.clone()); + let mut destination = DropWriter::new(ctx.path.clone()).await; if ctx.offset != 0 { destination .seek(SeekFrom::Start(ctx.offset)) + .await .expect("Failed to seek to file offset"); } @@ -168,7 +171,7 @@ pub fn download_game_chunk( if content_length.is_none() { warn!("recieved 0 length content from server"); return Err(ApplicationDownloadError::Communication( - RemoteAccessError::InvalidResponse(response.json().unwrap()), + RemoteAccessError::InvalidResponse(response.json().await.unwrap()), )); } @@ -178,11 +181,17 @@ pub fn download_game_chunk( return Err(ApplicationDownloadError::DownloadError); } + let response_stream = StreamReader::new( + response + .bytes_stream() + .map_err(|e| std::io::Error::other(e)), + ); let mut pipeline = - DropDownloadPipeline::new(response, destination, control_flag, progress, length); + DropDownloadPipeline::new(response_stream, destination, control_flag, progress, length); let completed = pipeline .copy() + .await .map_err(|e| ApplicationDownloadError::IoError(e.kind()))?; if !completed { return Ok(false); @@ -197,6 +206,7 @@ pub fn download_game_chunk( let checksum = pipeline .finish() + .await .map_err(|e| ApplicationDownloadError::IoError(e.kind()))?; let res = hex::encode(checksum.0); diff --git a/src-tauri/src/games/downloads/validate.rs b/src-tauri/src/games/downloads/validate.rs index c92a106..2543020 100644 --- a/src-tauri/src/games/downloads/validate.rs +++ b/src-tauri/src/games/downloads/validate.rs @@ -1,12 +1,11 @@ use std::{ fs::File, io::{self, BufWriter, Read, Seek, SeekFrom, Write}, - sync::{mpsc::Sender, Arc}, + sync::{Arc, mpsc::Sender}, }; use log::{debug, error, info}; use md5::Context; -use rayon::ThreadPoolBuilder; use crate::{ database::db::borrow_db_checked, @@ -21,7 +20,7 @@ use crate::{ games::downloads::{drop_data::DropData, manifest::DropDownloadContext}, }; -pub fn game_validate_logic( +pub async fn game_validate_logic( dropdata: &DropData, contexts: Vec, progress: Arc, @@ -29,49 +28,47 @@ pub fn game_validate_logic( control_flag: &DownloadThreadControl, ) -> Result { progress.reset(contexts.len()); - let max_download_threads = borrow_db_checked().settings.max_download_threads; + let max_download_threads = borrow_db_checked().await.settings.max_download_threads; debug!( "validating game: {} with {} threads", dropdata.game_id, max_download_threads ); - let pool = ThreadPoolBuilder::new() - .num_threads(max_download_threads) - .build() - .unwrap(); debug!("{contexts:#?}"); let invalid_chunks = Arc::new(boxcar::Vec::new()); - pool.scope(|scope| { - for (index, context) in contexts.iter().enumerate() { - let current_progress = progress.get(index); - let progress_handle = ProgressHandle::new(current_progress, progress.clone()); - let invalid_chunks_scoped = invalid_chunks.clone(); - let sender = sender.clone(); + unsafe { + async_scoped::TokioScope::scope_and_collect(|scope| { + for (index, context) in contexts.iter().enumerate() { + let current_progress = progress.get(index); + let progress_handle = ProgressHandle::new(current_progress, progress.clone()); + let invalid_chunks_scoped = invalid_chunks.clone(); + let sender = sender.clone(); - scope.spawn(move |_| { - match validate_game_chunk(context, control_flag, progress_handle) { - Ok(true) => { - debug!( - "Finished context #{} with checksum {}", - index, context.checksum - ); + scope.spawn(async move { + match validate_game_chunk(context, control_flag, progress_handle) { + Ok(true) => { + debug!( + "Finished context #{} with checksum {}", + index, context.checksum + ); + } + Ok(false) => { + debug!( + "Didn't finish context #{} with checksum {}", + index, &context.checksum + ); + invalid_chunks_scoped.push(context.checksum.clone()); + } + Err(e) => { + error!("{e}"); + sender.send(DownloadManagerSignal::Error(e)).unwrap(); + } } - Ok(false) => { - debug!( - "Didn't finish context #{} with checksum {}", - index, &context.checksum - ); - invalid_chunks_scoped.push(context.checksum.clone()); - } - Err(e) => { - error!("{e}"); - sender.send(DownloadManagerSignal::Error(e)).unwrap(); - } - } - }); - } - }); + }); + } + }).await + }; // If there are any contexts left which are false if !invalid_chunks.is_empty() { diff --git a/src-tauri/src/games/library.rs b/src-tauri/src/games/library.rs index 53041ad..2cbcf68 100644 --- a/src-tauri/src/games/library.rs +++ b/src-tauri/src/games/library.rs @@ -1,25 +1,24 @@ use std::fs::remove_dir_all; -use std::sync::Mutex; -use std::thread::spawn; use log::{debug, error, warn}; use serde::{Deserialize, Serialize}; use tauri::AppHandle; use tauri::Emitter; +use tokio::spawn; +use tokio::sync::Mutex; +use crate::AppState; use crate::database::db::{borrow_db_checked, borrow_db_mut_checked}; use crate::database::models::data::{ ApplicationTransientStatus, DownloadableMetadata, GameDownloadStatus, GameVersion, }; use crate::download_manager::download_manager_frontend::DownloadStatus; -use crate::error::library_error::LibraryError; use crate::error::remote_access_error::RemoteAccessError; use crate::games::state::{GameStatusManager, GameStatusWithTransient}; use crate::remote::auth::generate_authorization_header; use crate::remote::cache::{cache_object, get_cached_object, get_cached_object_db}; use crate::remote::requests::make_request; -use crate::AppState; -use bitcode::{Encode, Decode}; +use bitcode::{Decode, Encode}; #[derive(Serialize, Deserialize, Debug)] pub struct FetchGameStruct { @@ -73,28 +72,30 @@ pub struct StatsUpdateEvent { pub time: usize, } -pub fn fetch_library_logic( - state: tauri::State<'_, Mutex>, +pub async fn fetch_library_logic( + state: tauri::State<'_, Mutex>>, ) -> Result, RemoteAccessError> { - let header = generate_authorization_header(); + let header = generate_authorization_header().await; - let client = reqwest::blocking::Client::new(); - let response = make_request(&client, &["/api/v1/client/user/library"], &[], |f| { + let client = reqwest::Client::new(); + let response = make_request(&client, &["/api/v1/client/user/library"], &[], async |f| { f.header("Authorization", header) - })? - .send()?; + }) + .await? + .send() + .await?; if response.status() != 200 { - let err = response.json().unwrap(); + let err = response.json().await.unwrap(); warn!("{err:?}"); return Err(RemoteAccessError::InvalidResponse(err)); } - let mut games: Vec = response.json()?; + let mut games: Vec = response.json().await?; - let mut handle = state.lock().unwrap(); + let mut handle = state.lock().await; - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; for game in games.iter() { handle.games.insert(game.id.clone(), game.clone()); @@ -113,22 +114,22 @@ pub fn fetch_library_logic( } // We should always have a cache of the object // Pass db_handle because otherwise we get a gridlock - let game = get_cached_object_db::(meta.id.clone(), &db_handle)?; + let game = get_cached_object_db::(meta.id.clone(), &db_handle).await?; games.push(game); } drop(handle); drop(db_handle); - cache_object("library", &games)?; + cache_object("library", &games).await?; Ok(games) } -pub fn fetch_library_logic_offline( - _state: tauri::State<'_, Mutex>, +pub async fn fetch_library_logic_offline( + _state: tauri::State<'_, Mutex>>, ) -> Result, RemoteAccessError> { - let mut games: Vec = get_cached_object("library")?; + let mut games: Vec = get_cached_object("library").await?; - let db_handle = borrow_db_checked(); + let db_handle = borrow_db_checked().await; games.retain(|game| { db_handle @@ -139,13 +140,13 @@ pub fn fetch_library_logic_offline( Ok(games) } -pub fn fetch_game_logic( +pub async fn fetch_game_logic( id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result { - let mut state_handle = state.lock().unwrap(); + let mut state_handle = state.lock().await; - let handle = borrow_db_checked(); + let handle = borrow_db_checked().await; let metadata_option = handle.applications.installed_game_version.get(&id); let version = match metadata_option { @@ -165,7 +166,7 @@ pub fn fetch_game_logic( let game = state_handle.games.get(&id); if let Some(game) = game { - let status = GameStatusManager::fetch_state(&id); + let status = GameStatusManager::fetch_state(&id).await; let data = FetchGameStruct { game: game.clone(), @@ -173,29 +174,31 @@ pub fn fetch_game_logic( version, }; - cache_object(id, game)?; + cache_object(id, game).await?; return Ok(data); } - let client = reqwest::blocking::Client::new(); - let response = make_request(&client, &["/api/v1/client/game/", &id], &[], |r| { - r.header("Authorization", generate_authorization_header()) - })? - .send()?; + let client = reqwest::Client::new(); + let response = make_request(&client, &["/api/v1/client/game/", &id], &[], async |r| { + r.header("Authorization", generate_authorization_header().await) + }) + .await? + .send() + .await?; if response.status() == 404 { return Err(RemoteAccessError::GameNotFound(id)); } if response.status() != 200 { - let err = response.json().unwrap(); + let err = response.json().await.unwrap(); warn!("{err:?}"); return Err(RemoteAccessError::InvalidResponse(err)); } - let game: Game = response.json()?; + let game: Game = response.json().await?; state_handle.games.insert(id.clone(), game.clone()); - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; db_handle .applications @@ -204,7 +207,7 @@ pub fn fetch_game_logic( .or_insert(GameDownloadStatus::Remote {}); drop(db_handle); - let status = GameStatusManager::fetch_state(&id); + let status = GameStatusManager::fetch_state(&id).await; let data = FetchGameStruct { game: game.clone(), @@ -212,16 +215,16 @@ pub fn fetch_game_logic( version, }; - cache_object(id, &game)?; + cache_object(id, &game).await?; Ok(data) } -pub fn fetch_game_logic_offline( +pub async fn fetch_game_logic_offline( id: String, - _state: tauri::State<'_, Mutex>, + _state: tauri::State<'_, Mutex>>, ) -> Result { - let handle = borrow_db_checked(); + let handle = borrow_db_checked().await; let metadata_option = handle.applications.installed_game_version.get(&id); let version = match metadata_option { None => None, @@ -238,8 +241,8 @@ pub fn fetch_game_logic_offline( }; drop(handle); - let status = GameStatusManager::fetch_state(&id); - let game = get_cached_object::(id)?; + let status = GameStatusManager::fetch_state(&id).await; + let game = get_cached_object::(id).await?; Ok(FetchGameStruct { game, @@ -248,30 +251,32 @@ pub fn fetch_game_logic_offline( }) } -pub fn fetch_game_verion_options_logic( +pub async fn fetch_game_verion_options_logic( game_id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result, RemoteAccessError> { - let client = reqwest::blocking::Client::new(); + let client = reqwest::Client::new(); let response = make_request( &client, &["/api/v1/client/game/versions"], &[("id", &game_id)], - |r| r.header("Authorization", generate_authorization_header()), - )? - .send()?; + async |r| r.header("Authorization", generate_authorization_header().await), + ) + .await? + .send() + .await?; if response.status() != 200 { - let err = response.json().unwrap(); + let err = response.json().await.unwrap(); warn!("{err:?}"); return Err(RemoteAccessError::InvalidResponse(err)); } - let data: Vec = response.json()?; + let data: Vec = response.json().await?; - let state_lock = state.lock().unwrap(); - let process_manager_lock = state_lock.process_manager.lock().unwrap(); + let state_lock = state.lock().await; + let process_manager_lock = state_lock.process_manager.lock().await; let data: Vec = data .into_iter() .filter(|v| process_manager_lock.valid_platform(&v.platform).unwrap()) @@ -282,9 +287,9 @@ pub fn fetch_game_verion_options_logic( Ok(data) } -pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle) { +pub async fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle) { debug!("triggered uninstall for agent"); - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; db_handle .applications .transient_statuses @@ -329,33 +334,35 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle) drop(db_handle); let app_handle = app_handle.clone(); - spawn(move || match remove_dir_all(install_dir) { - Err(e) => { - error!("{e}"); - } - Ok(_) => { - let mut db_handle = borrow_db_mut_checked(); - db_handle.applications.transient_statuses.remove(&meta); - db_handle - .applications - .installed_game_version - .remove(&meta.id); - db_handle - .applications - .game_statuses - .entry(meta.id.clone()) - .and_modify(|e| *e = GameDownloadStatus::Remote {}); - drop(db_handle); + spawn(async move { + match remove_dir_all(install_dir) { + Err(e) => { + error!("{e}"); + } + Ok(_) => { + let mut db_handle = borrow_db_mut_checked().await; + db_handle.applications.transient_statuses.remove(&meta); + db_handle + .applications + .installed_game_version + .remove(&meta.id); + db_handle + .applications + .game_statuses + .entry(meta.id.clone()) + .and_modify(|e| *e = GameDownloadStatus::Remote {}); + drop(db_handle); - debug!("uninstalled game id {}", &meta.id); - app_handle.emit("update_library", ()).unwrap(); + debug!("uninstalled game id {}", &meta.id); + app_handle.emit("update_library", ()).unwrap(); - push_game_update( - &app_handle, - &meta.id, - None, - (Some(GameDownloadStatus::Remote {}), None), - ); + push_game_update( + &app_handle, + &meta.id, + None, + (Some(GameDownloadStatus::Remote {}), None), + ); + } } }); } else { @@ -363,15 +370,16 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle) } } -pub fn get_current_meta(game_id: &String) -> Option { +pub async fn get_current_meta(game_id: &String) -> Option { borrow_db_checked() + .await .applications .installed_game_version .get(game_id) .cloned() } -pub fn on_game_incomplete( +pub async fn on_game_incomplete( meta: &DownloadableMetadata, install_dir: String, app_handle: &AppHandle, @@ -381,7 +389,7 @@ pub fn on_game_incomplete( return Err(RemoteAccessError::GameNotFound(meta.id.clone())); } - let client = reqwest::blocking::Client::new(); + let client = reqwest::Client::new(); let response = make_request( &client, &["/api/v1/client/game/version"], @@ -389,13 +397,15 @@ pub fn on_game_incomplete( ("id", &meta.id), ("version", meta.version.as_ref().unwrap()), ], - |f| f.header("Authorization", generate_authorization_header()), - )? - .send()?; + async |f| f.header("Authorization", generate_authorization_header().await), + ) + .await? + .send() + .await?; - let game_version: GameVersion = response.json()?; + let game_version: GameVersion = response.json().await?; - let mut handle = borrow_db_mut_checked(); + let mut handle = borrow_db_mut_checked().await; handle .applications .game_versions @@ -431,7 +441,7 @@ pub fn on_game_incomplete( Ok(()) } -pub fn on_game_complete( +pub async fn on_game_complete( meta: &DownloadableMetadata, install_dir: String, app_handle: &AppHandle, @@ -441,9 +451,9 @@ pub fn on_game_complete( return Err(RemoteAccessError::GameNotFound(meta.id.clone())); } - let header = generate_authorization_header(); + let header = generate_authorization_header().await; - let client = reqwest::blocking::Client::new(); + let client = reqwest::Client::new(); let response = make_request( &client, &["/api/v1/client/game/version"], @@ -451,13 +461,15 @@ pub fn on_game_complete( ("id", &meta.id), ("version", meta.version.as_ref().unwrap()), ], - |f| f.header("Authorization", header), - )? - .send()?; + async |f| f.header("Authorization", header), + ) + .await? + .send() + .await?; - let game_version: GameVersion = response.json()?; + let game_version: GameVersion = response.json().await?; - let mut handle = borrow_db_mut_checked(); + let mut handle = borrow_db_mut_checked().await; handle .applications .game_versions @@ -483,7 +495,7 @@ pub fn on_game_complete( } }; - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; db_handle .applications .game_statuses @@ -520,48 +532,3 @@ pub fn push_game_update( ) .unwrap(); } - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct FrontendGameOptions { - launch_string: String, -} - -#[tauri::command] -pub fn update_game_configuration( - game_id: String, - options: FrontendGameOptions, -) -> Result<(), LibraryError> { - let mut handle = borrow_db_mut_checked(); - let installed_version = handle - .applications - .installed_game_version - .get(&game_id) - .ok_or(LibraryError::MetaNotFound(game_id))?; - - let id = installed_version.id.clone(); - let version = installed_version.version.clone().unwrap(); - - let mut existing_configuration = handle - .applications - .game_versions - .get(&id) - .unwrap() - .get(&version) - .unwrap() - .clone(); - - // Add more options in here - existing_configuration.launch_command_template = options.launch_string; - - // Add no more options past here - - handle - .applications - .game_versions - .get_mut(&id) - .unwrap() - .insert(version.to_string(), existing_configuration); - - Ok(()) -} diff --git a/src-tauri/src/games/state.rs b/src-tauri/src/games/state.rs index c95c0d5..aaa0060 100644 --- a/src-tauri/src/games/state.rs +++ b/src-tauri/src/games/state.rs @@ -10,8 +10,8 @@ pub type GameStatusWithTransient = ( pub struct GameStatusManager {} impl GameStatusManager { - pub fn fetch_state(game_id: &String) -> GameStatusWithTransient { - let db_lock = borrow_db_checked(); + pub async fn fetch_state(game_id: &String) -> GameStatusWithTransient { + let db_lock = borrow_db_checked().await; let online_state = match db_lock.applications.installed_game_version.get(game_id) { Some(meta) => db_lock.applications.transient_statuses.get(meta).cloned(), None => None, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 838ebd9..32bb261 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,6 @@ #![feature(fn_traits)] #![feature(duration_constructors)] +#![feature(impl_trait_in_assoc_type)] #![deny(clippy::all)] mod database; @@ -11,8 +12,11 @@ mod error; mod process; mod remote; +use crate::database::db::OnceCellDatabase; +use crate::games::commands::update_game_configuration; use crate::process::commands::open_process_logs; use crate::{database::db::DatabaseImpls, games::downloads::commands::resume_download}; +use bitcode::{Decode, Encode}; use client::commands::fetch_state; use client::{ autostart::{get_autostart_enabled, sync_autostart_on_startup, toggle_autostart}, @@ -22,7 +26,7 @@ use database::commands::{ add_download_dir, delete_download_dir, fetch_download_dir_stats, fetch_settings, fetch_system_data, update_settings, }; -use database::db::{borrow_db_checked, borrow_db_mut_checked, DatabaseInterface, DATA_ROOT_DIR}; +use database::db::{DATA_ROOT_DIR, DatabaseInterface, borrow_db_checked, borrow_db_mut_checked}; use database::models::data::GameDownloadStatus; use download_manager::commands::{ cancel_game, move_download_in_queue, pause_downloads, resume_downloads, @@ -37,13 +41,13 @@ use games::commands::{ fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, uninstall_game, }; use games::downloads::commands::download_game; -use games::library::{update_game_configuration, Game}; -use log::{debug, info, warn, LevelFilter}; +use games::library::Game; +use log::{LevelFilter, debug, info, warn}; +use log4rs::Config; use log4rs::append::console::ConsoleAppender; use log4rs::append::file::FileAppender; use log4rs::config::{Appender, Root}; use log4rs::encode::pattern::PatternEncoder; -use log4rs::Config; use process::commands::{kill_game, launch_game}; use process::process_manager::ProcessManager; use remote::auth::{self, recieve_handshake}; @@ -61,17 +65,14 @@ use std::path::Path; use std::str::FromStr; use std::sync::Arc; use std::time::SystemTime; -use std::{ - collections::HashMap, - sync::{LazyLock, Mutex}, -}; +use std::collections::HashMap; use std::{env, panic}; use tauri::menu::{Menu, MenuItem, PredefinedMenuItem}; use tauri::tray::TrayIconBuilder; use tauri::{AppHandle, Manager, RunEvent, WindowEvent}; use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_dialog::DialogExt; -use bitcode::{Encode, Decode}; +use tokio::sync::Mutex; #[derive(Clone, Copy, Serialize, Eq, PartialEq)] pub enum AppStatus { @@ -107,7 +108,7 @@ pub struct AppState<'a> { process_manager: Arc>>, } -fn setup(handle: AppHandle) -> AppState<'static> { +async fn setup(handle: AppHandle) -> AppState<'static> { let logfile = FileAppender::builder() .encoder(Box::new(PatternEncoder::new( "{d} | {l} | {f}:{L} - {m}{n}", @@ -143,7 +144,7 @@ fn setup(handle: AppHandle) -> AppState<'static> { let process_manager = Arc::new(Mutex::new(ProcessManager::new(handle.clone()))); debug!("checking if database is set up"); - let is_set_up = DB.database_is_set_up(); + let is_set_up = DB.database_is_set_up().await; if !is_set_up { return AppState { status: AppStatus::NotConfigured, @@ -157,9 +158,9 @@ fn setup(handle: AppHandle) -> AppState<'static> { debug!("database is set up"); // TODO: Account for possible failure - let (app_status, user) = auth::setup(); + let (app_status, user) = auth::setup().await; - let db_handle = borrow_db_checked(); + let db_handle = borrow_db_checked().await; let mut missing_games = Vec::new(); let statuses = db_handle.applications.game_statuses.clone(); drop(db_handle); @@ -190,7 +191,7 @@ fn setup(handle: AppHandle) -> AppState<'static> { info!("detected games missing: {missing_games:?}"); - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; for game_id in missing_games { db_handle .applications @@ -204,7 +205,7 @@ fn setup(handle: AppHandle) -> AppState<'static> { debug!("finished setup!"); // Sync autostart state - if let Err(e) = sync_autostart_on_startup(&handle) { + if let Err(e) = sync_autostart_on_startup(&handle).await { warn!("failed to sync autostart state: {e}"); } @@ -217,7 +218,7 @@ fn setup(handle: AppHandle) -> AppState<'static> { } } -pub static DB: LazyLock = LazyLock::new(DatabaseInterface::set_up_database); +pub static DB: OnceCellDatabase = OnceCellDatabase::new(); pub fn custom_panic_handler(e: &PanicHookInfo) -> Option<()> { let crash_file = DATA_ROOT_DIR.join(format!( @@ -236,12 +237,15 @@ pub fn custom_panic_handler(e: &PanicHookInfo) -> Option<()> { } #[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { +pub async fn run() { panic::set_hook(Box::new(|e| { let _ = custom_panic_handler(e); println!("{e}"); })); + DB.init(async { DatabaseInterface::set_up_database().await }) + .await; + let mut builder = tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) @@ -312,114 +316,131 @@ pub fn run() { Some(vec!["--minimize"]), )) .setup(|app| { - let handle = app.handle().clone(); - let state = setup(handle); - debug!("initialized drop client"); - app.manage(Mutex::new(state)); + let app = app.handle().clone(); - { - use tauri_plugin_deep_link::DeepLinkExt; - let _ = app.deep_link().register_all(); - debug!("registered all pre-defined deep links"); - } + tauri::async_runtime::spawn(async move { + let state = setup(app.clone()).await; + debug!("initialized drop client"); + app.manage(Mutex::new(state)); - let handle = app.handle().clone(); - - let _main_window = tauri::WebviewWindowBuilder::new( - &handle, - "main", // BTW this is not the name of the window, just the label. Keep this 'main', there are permissions & configs that depend on it - tauri::WebviewUrl::App("index.html".into()), - ) - .title("Drop Desktop App") - .min_inner_size(1000.0, 500.0) - .inner_size(1536.0, 864.0) - .decorations(false) - .shadow(false) - .data_directory(DATA_ROOT_DIR.join(".webview")) - .build() - .unwrap(); - - app.deep_link().on_open_url(move |event| { - debug!("handling drop:// url"); - let binding = event.urls(); - let url = binding.first().unwrap(); - if url.host_str().unwrap() == "handshake" { - recieve_handshake(handle.clone(), url.path().to_string()) + { + use tauri_plugin_deep_link::DeepLinkExt; + let _ = app.deep_link().register_all(); + debug!("registered all pre-defined deep links"); } + + let _main_window = tauri::WebviewWindowBuilder::new( + &app, + "main", // BTW this is not the name of the window, just the label. Keep this 'main', there are permissions & configs that depend on it + tauri::WebviewUrl::App("index.html".into()), + ) + .title("Drop Desktop App") + .min_inner_size(1000.0, 500.0) + .inner_size(1536.0, 864.0) + .decorations(false) + .shadow(false) + .data_directory(DATA_ROOT_DIR.join(".webview")) + .build() + .unwrap(); + + let deep_link_handle = app.clone(); + + app.deep_link().on_open_url(move |event| { + let deep_link_handle = deep_link_handle.clone(); + + tauri::async_runtime::block_on(async move { + debug!("handling drop:// url"); + let binding = event.urls(); + let url = binding.first().unwrap(); + if url.host_str().unwrap() == "handshake" { + recieve_handshake(deep_link_handle, url.path().to_string()).await + } + }); + }); + + let menu = Menu::with_items( + &app, + &[ + &MenuItem::with_id(&app, "open", "Open", true, None::<&str>).unwrap(), + &PredefinedMenuItem::separator(&app).unwrap(), + /* + &MenuItem::with_id(app, "show_library", "Library", true, None::<&str>)?, + &MenuItem::with_id(app, "show_settings", "Settings", true, None::<&str>)?, + &PredefinedMenuItem::separator(app)?, + */ + &MenuItem::with_id(&app, "quit", "Quit", true, None::<&str>).unwrap(), + ], + ) + .unwrap(); + + run_on_tray(|| { + TrayIconBuilder::new() + .icon(app.default_window_icon().unwrap().clone()) + .menu(&menu) + .on_menu_event(|app, event| { + tauri::async_runtime::block_on(async move { + match event.id.as_ref() { + "open" => { + app.webview_windows().get("main").unwrap().show().unwrap(); + } + "quit" => { + cleanup_and_exit(app, &app.state()).await; + } + + _ => { + warn!("menu event not handled: {:?}", event.id); + } + } + }) + }) + .build(&app) + .expect("error while setting up tray menu"); + }); + + { + let mut db_handle = borrow_db_mut_checked().await; + if let Some(original) = db_handle.prev_database.take() { + warn!( + "Database corrupted. Original file at {}", + original.canonicalize().unwrap().to_string_lossy() + ); + app.dialog() + .message( + "Database corrupted. A copy has been saved at: ".to_string() + + original.to_str().unwrap(), + ) + .title("Database corrupted") + .show(|_| {}); + } + }; }); - - let menu = Menu::with_items( - app, - &[ - &MenuItem::with_id(app, "open", "Open", true, None::<&str>)?, - &PredefinedMenuItem::separator(app)?, - /* - &MenuItem::with_id(app, "show_library", "Library", true, None::<&str>)?, - &MenuItem::with_id(app, "show_settings", "Settings", true, None::<&str>)?, - &PredefinedMenuItem::separator(app)?, - */ - &MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?, - ], - )?; - - run_on_tray(|| { - TrayIconBuilder::new() - .icon(app.default_window_icon().unwrap().clone()) - .menu(&menu) - .on_menu_event(|app, event| match event.id.as_ref() { - "open" => { - app.webview_windows().get("main").unwrap().show().unwrap(); - } - "quit" => { - cleanup_and_exit(app, &app.state()); - } - - _ => { - warn!("menu event not handled: {:?}", event.id); - } - }) - .build(app) - .expect("error while setting up tray menu"); - }); - - { - let mut db_handle = borrow_db_mut_checked(); - if let Some(original) = db_handle.prev_database.take() { - warn!( - "Database corrupted. Original file at {}", - original.canonicalize().unwrap().to_string_lossy() - ); - app.dialog() - .message( - "Database corrupted. A copy has been saved at: ".to_string() - + original.to_str().unwrap(), - ) - .title("Database corrupted") - .show(|_| {}); - } - } - Ok(()) }) .register_asynchronous_uri_scheme_protocol("object", move |ctx, request, responder| { - let state: tauri::State<'_, Mutex> = ctx.app_handle().state(); - offline!( - state, - fetch_object, - fetch_object_offline, - request, - responder - ); + tauri::async_runtime::block_on(async move { + let state: tauri::State<'_, Mutex> = ctx.app_handle().state(); + offline!( + state, + fetch_object, + fetch_object_offline, + request, + responder + ) + .await; + }); }) .register_asynchronous_uri_scheme_protocol("server", move |ctx, request, responder| { - let state: tauri::State<'_, Mutex> = ctx.app_handle().state(); - offline!( - state, - handle_server_proto, - handle_server_proto_offline, - request, - responder - ); + tauri::async_runtime::block_on(async move { + let state: tauri::State<'_, Mutex> = ctx.app_handle().state(); + offline!( + state, + handle_server_proto, + handle_server_proto_offline, + request, + responder + ) + .await; + }); }) .on_window_event(|window, event| { if let WindowEvent::CloseRequested { api, .. } = event { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a6a1453..6f28672 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,6 +1,7 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -fn main() { - drop_app_lib::run() +#[tokio::main] +async fn main() { + drop_app_lib::run().await } diff --git a/src-tauri/src/process/commands.rs b/src-tauri/src/process/commands.rs index 93cd86a..a9ef333 100644 --- a/src-tauri/src/process/commands.rs +++ b/src-tauri/src/process/commands.rs @@ -1,14 +1,14 @@ -use std::sync::Mutex; +use tokio::sync::Mutex; -use crate::{error::process_error::ProcessError, AppState}; +use crate::{AppState, error::process_error::ProcessError}; #[tauri::command] -pub fn launch_game( +pub async fn launch_game( id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result<(), ProcessError> { - let state_lock = state.lock().unwrap(); - let mut process_manager_lock = state_lock.process_manager.lock().unwrap(); + let state_lock = state.lock().await; + let mut process_manager_lock = state_lock.process_manager.lock().await; //let meta = DownloadableMetadata { // id, @@ -16,7 +16,7 @@ pub fn launch_game( // download_type: DownloadType::Game, //}; - match process_manager_lock.launch_process(id) { + match process_manager_lock.launch_process(id).await { Ok(_) => {} Err(e) => return Err(e), }; @@ -28,23 +28,23 @@ pub fn launch_game( } #[tauri::command] -pub fn kill_game( +pub async fn kill_game( game_id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result<(), ProcessError> { - let state_lock = state.lock().unwrap(); - let mut process_manager_lock = state_lock.process_manager.lock().unwrap(); + let state_lock = state.lock().await; + let mut process_manager_lock = state_lock.process_manager.lock().await; process_manager_lock .kill_game(game_id) .map_err(ProcessError::IOError) } #[tauri::command] -pub fn open_process_logs( +pub async fn open_process_logs( game_id: String, - state: tauri::State<'_, Mutex>, + state: tauri::State<'_, Mutex>>, ) -> Result<(), ProcessError> { - let state_lock = state.lock().unwrap(); - let mut process_manager_lock = state_lock.process_manager.lock().unwrap(); + let state_lock = state.lock().await; + let mut process_manager_lock = state_lock.process_manager.lock().await; process_manager_lock.open_process_logs(game_id) } diff --git a/src-tauri/src/process/process_manager.rs b/src-tauri/src/process/process_manager.rs index 6eb45c8..0e58493 100644 --- a/src-tauri/src/process/process_manager.rs +++ b/src-tauri/src/process/process_manager.rs @@ -5,8 +5,7 @@ use std::{ path::PathBuf, process::{Command, ExitStatus}, str::FromStr, - sync::{Arc, Mutex}, - thread::spawn, + sync::{Arc}, time::{Duration, SystemTime}, }; @@ -17,6 +16,7 @@ use serde::{Deserialize, Serialize}; use shared_child::SharedChild; use tauri::{AppHandle, Emitter, Manager}; use tauri_plugin_opener::OpenerExt; +use tokio::{spawn, sync::Mutex}; use crate::{ AppState, DB, @@ -108,7 +108,7 @@ impl ProcessManager<'_> { Ok(()) } - fn on_process_finish(&mut self, game_id: String, result: Result) { + async fn on_process_finish(&mut self, game_id: String, result: Result) { if !self.processes.contains_key(&game_id) { warn!( "process on_finish was called, but game_id is no longer valid. finished with result: {result:?}" @@ -120,7 +120,7 @@ impl ProcessManager<'_> { let process = self.processes.remove(&game_id).unwrap(); - let mut db_handle = borrow_db_mut_checked(); + let mut db_handle = borrow_db_mut_checked().await; let meta = db_handle .applications .installed_game_version @@ -158,7 +158,7 @@ impl ProcessManager<'_> { let _ = self.app_handle.emit("launch_external_error", &game_id); } - let status = GameStatusManager::fetch_state(&game_id); + let status = GameStatusManager::fetch_state(&game_id).await; push_game_update(&self.app_handle, &game_id, None, status); } @@ -167,14 +167,14 @@ impl ProcessManager<'_> { Ok(self.game_launchers.contains_key(&(*current, *platform))) } - pub fn launch_process(&mut self, game_id: String) -> Result<(), ProcessError> { + pub async fn launch_process(&mut self, game_id: String) -> Result<(), ProcessError> { if self.processes.contains_key(&game_id) { return Err(ProcessError::AlreadyRunning); } let version = match DB .borrow_data() - .unwrap() + .await .applications .game_statuses .get(&game_id) @@ -192,7 +192,7 @@ impl ProcessManager<'_> { download_type: DownloadType::Game, }; - let mut db_lock = borrow_db_mut_checked(); + let mut db_lock = borrow_db_mut_checked().await; debug!( "Launching process {:?} with games {:?}", &game_id, db_lock.applications.game_versions @@ -334,14 +334,14 @@ impl ProcessManager<'_> { let wait_thread_apphandle = self.app_handle.clone(); let wait_thread_game_id = meta.clone(); - spawn(move || { + spawn(async move { let result: Result = launch_process_handle.wait(); let app_state = wait_thread_apphandle.state::>(); - let app_state_handle = app_state.lock().unwrap(); + let app_state_handle = app_state.lock().await; - let mut process_manager_handle = app_state_handle.process_manager.lock().unwrap(); - process_manager_handle.on_process_finish(wait_thread_game_id.id, result); + let mut process_manager_handle = app_state_handle.process_manager.lock().await; + process_manager_handle.on_process_finish(wait_thread_game_id.id, result).await; // As everything goes out of scope, they should get dropped // But just to explicit about it diff --git a/src-tauri/src/remote/auth.rs b/src-tauri/src/remote/auth.rs index cb64350..51e2488 100644 --- a/src-tauri/src/remote/auth.rs +++ b/src-tauri/src/remote/auth.rs @@ -9,12 +9,12 @@ use tauri::{AppHandle, Emitter, Manager}; use url::Url; use crate::{ + AppState, AppStatus, User, database::{ db::{borrow_db_checked, borrow_db_mut_checked}, models::data::DatabaseAuth, }, error::{drop_server_error::DropServerError, remote_access_error::RemoteAccessError}, - AppState, AppStatus, User, }; use super::{ @@ -49,9 +49,9 @@ struct HandshakeResponse { id: String, } -pub fn generate_authorization_header() -> String { +pub async fn generate_authorization_header() -> String { let certs = { - let db = borrow_db_checked(); + let db = borrow_db_checked().await; db.auth.clone().unwrap() }; @@ -62,16 +62,18 @@ pub fn generate_authorization_header() -> String { format!("Nonce {} {} {}", certs.client_id, nonce, signature) } -pub fn fetch_user() -> Result { - let header = generate_authorization_header(); +pub async fn fetch_user() -> Result { + let header = generate_authorization_header().await; - let client = reqwest::blocking::Client::new(); - let response = make_request(&client, &["/api/v1/client/user"], &[], |f| { + let client = reqwest::Client::new(); + let response = make_request(&client, &["/api/v1/client/user"], &[], async |f| { f.header("Authorization", header) - })? - .send()?; + }) + .await? + .send() + .await?; if response.status() != 200 { - let err: DropServerError = response.json()?; + let err: DropServerError = response.json().await?; warn!("{err:?}"); if err.status_message == "Nonce expired" { @@ -81,10 +83,10 @@ pub fn fetch_user() -> Result { return Err(RemoteAccessError::InvalidResponse(err)); } - response.json::().map_err(|e| e.into()) + response.json::().await.map_err(|e| e.into()) } -fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAccessError> { +async fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAccessError> { let path_chunks: Vec<&str> = path.split("/").collect(); if path_chunks.len() != 3 { app.emit("auth/failed", ()).unwrap(); @@ -94,7 +96,7 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc } let base_url = { - let handle = borrow_db_checked(); + let handle = borrow_db_checked().await; Url::parse(handle.base_url.as_str())? }; @@ -106,16 +108,16 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc }; let endpoint = base_url.join("/api/v1/client/auth/handshake")?; - let client = reqwest::blocking::Client::new(); - let response = client.post(endpoint).json(&body).send()?; + let client = reqwest::Client::new(); + let response = client.post(endpoint).json(&body).send().await?; debug!("handshake responsded with {}", response.status().as_u16()); if !response.status().is_success() { - return Err(RemoteAccessError::InvalidResponse(response.json()?)); + return Err(RemoteAccessError::InvalidResponse(response.json().await?)); } - let response_struct: HandshakeResponse = response.json()?; + let response_struct: HandshakeResponse = response.json().await?; { - let mut handle = borrow_db_mut_checked(); + let mut handle = borrow_db_mut_checked().await; handle.auth = Some(DatabaseAuth { private: response_struct.private, cert: response_struct.certificate, @@ -125,28 +127,29 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc } let web_token = { - let header = generate_authorization_header(); + let header = generate_authorization_header().await; let token = client .post(base_url.join("/api/v1/client/user/webtoken").unwrap()) .header("Authorization", header) .send() + .await .unwrap(); - token.text().unwrap() + token.text().await.unwrap() }; - let mut handle = borrow_db_mut_checked(); + let mut handle = borrow_db_mut_checked().await; let mut_auth = handle.auth.as_mut().unwrap(); mut_auth.web_token = Some(web_token); Ok(()) } -pub fn recieve_handshake(app: AppHandle, path: String) { +pub async fn recieve_handshake(app: AppHandle, path: String) { // Tell the app we're processing app.emit("auth/processing", ()).unwrap(); - let handshake_result = recieve_handshake_logic(&app, path); + let handshake_result = recieve_handshake_logic(&app, path).await; if let Err(e) = handshake_result { warn!("error with authentication: {e}"); app.emit("auth/failed", e.to_string()).unwrap(); @@ -154,21 +157,19 @@ pub fn recieve_handshake(app: AppHandle, path: String) { } let app_state = app.state::>(); + + let (app_status, user) = setup().await; + let mut state_lock = app_state.lock().unwrap(); - - let (app_status, user) = setup(); - state_lock.status = app_status; state_lock.user = user; - drop(state_lock); - app.emit("auth/finished", ()).unwrap(); } -pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> { +pub async fn auth_initiate_logic() -> Result<(), RemoteAccessError> { let base_url = { - let db_lock = borrow_db_checked(); + let db_lock = borrow_db_checked().await; Url::parse(&db_lock.base_url.clone())? }; @@ -203,21 +204,21 @@ pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> { Ok(()) } -pub fn setup() -> (AppStatus, Option) { - let data = borrow_db_checked(); +pub async fn setup() -> (AppStatus, Option) { + let data = borrow_db_checked().await; let auth = data.auth.clone(); drop(data); if auth.is_some() { - let user_result = match fetch_user() { + let user_result = match fetch_user().await { Ok(data) => data, Err(RemoteAccessError::FetchError(_)) => { - let user = get_cached_object::<_, User>("user").unwrap(); + let user = get_cached_object::<_, User>("user").await.unwrap(); return (AppStatus::Offline, Some(user)); } Err(_) => return (AppStatus::SignedInNeedsReauth, None), }; - cache_object("user", &user_result).unwrap(); + cache_object("user", &user_result).await.unwrap(); return (AppStatus::SignedIn, Some(user_result)); } diff --git a/src-tauri/src/remote/cache.rs b/src-tauri/src/remote/cache.rs index 9b73df5..4f15e4c 100644 --- a/src-tauri/src/remote/cache.rs +++ b/src-tauri/src/remote/cache.rs @@ -4,49 +4,64 @@ use std::{ }; use crate::{ - database::{db::borrow_db_checked, models::data::Database}, + database::{ + db::borrow_db_checked, + models::data::Database, + }, error::remote_access_error::RemoteAccessError, }; use bitcode::{Decode, DecodeOwned, Encode}; use cacache::Integrity; use http::{Response, header::CONTENT_TYPE, response::Builder as ResponseBuilder}; +use log::info; #[macro_export] macro_rules! offline { ($var:expr, $func1:expr, $func2:expr, $( $arg:expr ),* ) => { - if $crate::borrow_db_checked().settings.force_offline || $var.lock().unwrap().status == $crate::AppStatus::Offline { - $func2( $( $arg ), *) + (async || if $crate::borrow_db_checked().await.settings.force_offline || $var.lock().await.status == $crate::AppStatus::Offline { + $func2( $( $arg ), *).await } else { - $func1( $( $arg ), *) - } + $func1( $( $arg ), *).await + })() } } -pub fn cache_object, D: Encode>( +pub async fn cache_object, D: Encode>( key: K, data: &D, ) -> Result { let bytes = bitcode::encode(data); - cacache::write_sync(&borrow_db_checked().cache_dir, key, bytes) + cacache::write_sync(&borrow_db_checked().await.cache_dir, key, bytes) .map_err(RemoteAccessError::Cache) } -pub fn get_cached_object + Display, D: Encode + DecodeOwned>( +pub async fn get_cached_object + Display, D: Encode + DecodeOwned>( key: K, ) -> Result { - get_cached_object_db::(key, &borrow_db_checked()) + get_cached_object_db::(key, &&(borrow_db_checked().await)).await } -pub fn get_cached_object_db + Display, D: DecodeOwned>( +pub async fn get_cached_object_db<'a, K: AsRef + Display, D: DecodeOwned>( key: K, db: &Database, ) -> Result { - let bytes = cacache::read_sync(&db.cache_dir, &key).map_err(RemoteAccessError::Cache)?; + let start = SystemTime::now(); + let bytes = cacache::read(&db.cache_dir, &key) + .await + .map_err(RemoteAccessError::Cache)?; + let read = start.elapsed().unwrap(); let data = bitcode::decode::(&bytes).map_err(|_| { RemoteAccessError::Cache(cacache::Error::EntryNotFound( db.cache_dir.clone(), key.to_string(), )) })?; + let parse = start.elapsed().unwrap().abs_diff(read); + info!( + "read object: r: {}, p: {}, b: {}", + read.as_millis(), + parse.as_millis(), + bytes.len() + ); Ok(data) } #[derive(Encode, Decode)] diff --git a/src-tauri/src/remote/commands.rs b/src-tauri/src/remote/commands.rs index 2ba36f8..24db1a9 100644 --- a/src-tauri/src/remote/commands.rs +++ b/src-tauri/src/remote/commands.rs @@ -1,15 +1,15 @@ use std::sync::Mutex; use log::debug; -use reqwest::blocking::Client; +use reqwest::Client; use tauri::{AppHandle, Emitter, Manager}; use url::Url; use crate::{ + AppState, AppStatus, database::db::{borrow_db_checked, borrow_db_mut_checked}, error::remote_access_error::RemoteAccessError, remote::{auth::generate_authorization_header, requests::make_request}, - AppState, AppStatus, }; use super::{ @@ -19,17 +19,17 @@ use super::{ }; #[tauri::command] -pub fn use_remote( +pub async fn use_remote( url: String, state: tauri::State<'_, Mutex>>, ) -> Result<(), RemoteAccessError> { - use_remote_logic(url, state) + use_remote_logic(url, state).await } #[tauri::command] -pub fn gen_drop_url(path: String) -> Result { +pub async fn gen_drop_url(path: String) -> Result { let base_url = { - let handle = borrow_db_checked(); + let handle = borrow_db_checked().await; Url::parse(&handle.base_url).map_err(RemoteAccessError::ParsingError)? }; @@ -40,30 +40,32 @@ pub fn gen_drop_url(path: String) -> Result { } #[tauri::command] -pub fn fetch_drop_object(path: String) -> Result, RemoteAccessError> { - let _drop_url = gen_drop_url(path.clone())?; - let req = make_request(&Client::new(), &[&path], &[], |r| { - r.header("Authorization", generate_authorization_header()) - })? - .send(); +pub async fn fetch_drop_object(path: String) -> Result, RemoteAccessError> { + let _drop_url = gen_drop_url(path.clone()).await?; + let req = make_request(&Client::new(), &[&path], &[], async |r| { + r.header("Authorization", generate_authorization_header().await) + }) + .await? + .send() + .await; match req { Ok(data) => { - let data = data.bytes()?.to_vec(); - cache_object(&path, &data)?; + let data = data.bytes().await?.to_vec(); + cache_object(&path, &data).await?; Ok(data) } Err(e) => { debug!("{e}"); - get_cached_object::<&str, Vec>(&path) + get_cached_object::<&str, Vec>(&path).await } } } #[tauri::command] -pub fn sign_out(app: AppHandle) { +pub async fn sign_out(app: AppHandle) { // Clear auth from database { - let mut handle = borrow_db_mut_checked(); + let mut handle = borrow_db_mut_checked().await; handle.auth = None; } @@ -80,21 +82,23 @@ pub fn sign_out(app: AppHandle) { } #[tauri::command] -pub fn retry_connect(state: tauri::State<'_, Mutex>) { - let (app_status, user) = setup(); +pub async fn retry_connect(state: tauri::State<'_, Mutex>>) -> Result<(), ()> { + let (app_status, user) = setup().await; let mut guard = state.lock().unwrap(); guard.status = app_status; guard.user = user; drop(guard); + + Ok(()) } #[tauri::command] -pub fn auth_initiate() -> Result<(), RemoteAccessError> { - auth_initiate_logic() +pub async fn auth_initiate() -> Result<(), RemoteAccessError> { + auth_initiate_logic().await } #[tauri::command] -pub fn manual_recieve_handshake(app: AppHandle, token: String) { - recieve_handshake(app, format!("handshake/{token}")); +pub async fn manual_recieve_handshake(app: AppHandle, token: String) { + recieve_handshake(app, format!("handshake/{token}")).await; } diff --git a/src-tauri/src/remote/fetch_object.rs b/src-tauri/src/remote/fetch_object.rs index fbd3ac4..2be33e7 100644 --- a/src-tauri/src/remote/fetch_object.rs +++ b/src-tauri/src/remote/fetch_object.rs @@ -4,15 +4,15 @@ use tauri::UriSchemeResponder; use super::{ auth::generate_authorization_header, - cache::{cache_object, get_cached_object, ObjectCache}, + cache::{ObjectCache, cache_object, get_cached_object}, requests::make_request, }; -pub fn fetch_object(request: http::Request>, responder: UriSchemeResponder) { +pub async fn fetch_object(request: http::Request>, responder: UriSchemeResponder) { // Drop leading / let object_id = &request.uri().path()[1..]; - let cache_result = get_cached_object::<&str, ObjectCache>(object_id); + let cache_result = get_cached_object::<&str, ObjectCache>(object_id).await; if let Ok(cache_result) = &cache_result && !cache_result.has_expired() { @@ -20,13 +20,18 @@ pub fn fetch_object(request: http::Request>, responder: UriSchemeRespond return; } - let header = generate_authorization_header(); - let client: reqwest::blocking::Client = reqwest::blocking::Client::new(); - let response = make_request(&client, &["/api/v1/client/object/", object_id], &[], |f| { - f.header("Authorization", header) - }) + let header = generate_authorization_header().await; + let client = reqwest::Client::new(); + let response = make_request( + &client, + &["/api/v1/client/object/", object_id], + &[], + async |f| f.header("Authorization", header), + ) + .await .unwrap() - .send(); + .send() + .await; if response.is_err() { match cache_result { Ok(cache_result) => responder.respond(cache_result.into()), @@ -42,17 +47,19 @@ pub fn fetch_object(request: http::Request>, responder: UriSchemeRespond CONTENT_TYPE, response.headers().get("Content-Type").unwrap(), ); - let data = Vec::from(response.bytes().unwrap()); + let data = Vec::from(response.bytes().await.unwrap()); let resp = resp_builder.body(data).unwrap(); if cache_result.is_err() || cache_result.unwrap().has_expired() { - cache_object::<&str, ObjectCache>(object_id, &resp.clone().into()).unwrap(); + cache_object::<&str, ObjectCache>(object_id, &resp.clone().into()) + .await + .unwrap(); } responder.respond(resp); } -pub fn fetch_object_offline(request: http::Request>, responder: UriSchemeResponder) { +pub async fn fetch_object_offline(request: http::Request>, responder: UriSchemeResponder) { let object_id = &request.uri().path()[1..]; - let data = get_cached_object::<&str, ObjectCache>(object_id); + let data = get_cached_object::<&str, ObjectCache>(object_id).await; match data { Ok(data) => responder.respond(data.into()), diff --git a/src-tauri/src/remote/requests.rs b/src-tauri/src/remote/requests.rs index 44cdc83..3f7984d 100644 --- a/src-tauri/src/remote/requests.rs +++ b/src-tauri/src/remote/requests.rs @@ -1,14 +1,14 @@ -use reqwest::blocking::{Client, RequestBuilder}; +use reqwest::{Client, RequestBuilder}; use crate::{database::db::DatabaseImpls, error::remote_access_error::RemoteAccessError, DB}; -pub fn make_request, F: FnOnce(RequestBuilder) -> RequestBuilder>( +pub async fn make_request, F: AsyncFnOnce(RequestBuilder) -> RequestBuilder>( client: &Client, path_components: &[T], query: &[(T, T)], f: F, ) -> Result { - let mut base_url = DB.fetch_base_url(); + let mut base_url = DB.fetch_base_url().await; for endpoint in path_components { base_url = base_url.join(endpoint.as_ref())?; } @@ -19,5 +19,5 @@ pub fn make_request, F: FnOnce(RequestBuilder) -> RequestBuilder>( } } let response = client.get(base_url); - Ok(f(response)) + Ok(f(response).await) } diff --git a/src-tauri/src/remote/server_proto.rs b/src-tauri/src/remote/server_proto.rs index 26c0e15..854aa40 100644 --- a/src-tauri/src/remote/server_proto.rs +++ b/src-tauri/src/remote/server_proto.rs @@ -6,7 +6,7 @@ use tauri::UriSchemeResponder; use crate::database::db::borrow_db_checked; -pub fn handle_server_proto_offline(_request: Request>, responder: UriSchemeResponder) { +pub async fn handle_server_proto_offline(_request: Request>, responder: UriSchemeResponder) { let four_oh_four = Response::builder() .status(StatusCode::NOT_FOUND) .body(Vec::new()) @@ -14,8 +14,8 @@ pub fn handle_server_proto_offline(_request: Request>, responder: UriSch responder.respond(four_oh_four); } -pub fn handle_server_proto(request: Request>, responder: UriSchemeResponder) { - let db_handle = borrow_db_checked(); +pub async fn handle_server_proto(request: Request>, responder: UriSchemeResponder) { + let db_handle = borrow_db_checked().await; let web_token = match &db_handle.auth.as_ref().unwrap().web_token { Some(e) => e, None => return, diff --git a/src-tauri/src/remote/utils.rs b/src-tauri/src/remote/utils.rs index baf8dad..60b84ef 100644 --- a/src-tauri/src/remote/utils.rs +++ b/src-tauri/src/remote/utils.rs @@ -5,8 +5,8 @@ use serde::Deserialize; use url::Url; use crate::{ - database::db::borrow_db_mut_checked, error::remote_access_error::RemoteAccessError, AppState, - AppStatus, + AppState, AppStatus, database::db::borrow_db_mut_checked, + error::remote_access_error::RemoteAccessError, }; #[derive(Deserialize)] @@ -15,7 +15,7 @@ struct DropHealthcheck { app_name: String, } -pub fn use_remote_logic( +pub async fn use_remote_logic( url: String, state: tauri::State<'_, Mutex>>, ) -> Result<(), RemoteAccessError> { @@ -33,11 +33,12 @@ pub fn use_remote_logic( return Err(RemoteAccessError::InvalidEndpoint); } - let mut app_state = state.lock().unwrap(); - app_state.status = AppStatus::SignedOut; - drop(app_state); + { + let mut app_state = state.lock().unwrap(); + app_state.status = AppStatus::SignedOut; + } - let mut db_state = borrow_db_mut_checked(); + let mut db_state = borrow_db_mut_checked().await; db_state.base_url = base_url.to_string(); Ok(())