From 6fd9a6a082c8a3fbf052b40f0b429baec926dcd2 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sat, 20 Dec 2025 01:08:15 +1100 Subject: [PATCH 01/19] feat: depot api downloads --- src-tauri/Cargo.lock | 1077 +++++++++-------- src-tauri/Cargo.toml | 184 ++- src-tauri/client/Cargo.toml | 12 +- src-tauri/database/src/models.rs | 5 +- src-tauri/download_manager/Cargo.toml | 6 +- .../src/download_manager_builder.rs | 92 +- .../src/download_manager_frontend.rs | 43 +- .../download_manager/src/downloadable.rs | 6 +- .../src/util/progress_object.rs | 26 +- src-tauri/games/Cargo.toml | 12 +- .../games/src/downloads/download_agent.rs | 430 ++----- .../games/src/downloads/download_logic.rs | 305 ++--- src-tauri/games/src/downloads/manifest.rs | 11 - src-tauri/games/src/downloads/mod.rs | 3 +- src-tauri/games/src/downloads/validate.rs | 104 -- src-tauri/games/src/lib.rs | 1 + src-tauri/games/src/library.rs | 8 +- src-tauri/process/Cargo.toml | 4 +- src-tauri/remote/Cargo.toml | 2 +- src-tauri/remote/src/lib.rs | 3 + src-tauri/remote/src/requests.rs | 12 +- src-tauri/remote/src/server_proto.rs | 92 +- src-tauri/remote/src/utils.rs | 5 + src-tauri/src/client.rs | 19 +- src-tauri/src/download_manager.rs | 16 +- src-tauri/src/downloads.rs | 6 +- src-tauri/src/games.rs | 8 +- src-tauri/src/lib.rs | 29 +- src-tauri/utils/src/download_manager_send.rs | 2 +- 29 files changed, 1069 insertions(+), 1454 deletions(-) delete mode 100644 src-tauri/games/src/downloads/validate.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 62c6bcca..c252ef2c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -8,11 +8,22 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -120,7 +131,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] @@ -132,7 +143,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] @@ -144,7 +155,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -266,7 +277,18 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[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]] @@ -328,7 +350,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -450,7 +472,7 @@ checksum = "ffebfc2d28a12b262c303cb3860ee77b91bd83b1f20f0bd2a9693008e2f55a9e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -461,11 +483,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -486,7 +508,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", ] [[package]] @@ -495,7 +517,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", ] [[package]] @@ -507,22 +529,13 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2 0.5.2", -] - [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2 0.6.3", + "objc2", ] [[package]] @@ -591,9 +604,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] @@ -653,7 +666,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cairo-sys-rs", "glib", "libc", @@ -716,9 +729,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.41" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -779,6 +792,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "client" version = "0.1.0" @@ -897,7 +920,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-graphics-types", "foreign-types 0.5.0", @@ -910,7 +933,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "libc", ] @@ -975,11 +998,11 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", "typenum", ] @@ -1007,7 +1030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1017,7 +1040,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", ] [[package]] @@ -1041,7 +1073,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1052,7 +1084,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1112,9 +1144,9 @@ checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", "serde_core", @@ -1130,27 +1162,28 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "rustc_version", + "syn 2.0.111", "unicode-xid", ] @@ -1175,7 +1208,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.9", + "generic-array 0.14.7", ] [[package]] @@ -1241,10 +1274,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.10.0", + "block2", "libc", - "objc2 0.6.3", + "objc2", ] [[package]] @@ -1255,7 +1288,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1269,9 +1302,9 @@ dependencies = [ [[package]] name = "dlopen2" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "dlopen2_derive", "libc", @@ -1281,13 +1314,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1301,9 +1334,9 @@ dependencies = [ [[package]] name = "doc-comment" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "downcast-rs" @@ -1315,6 +1348,7 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" name = "download_manager" version = "0.1.0" dependencies = [ + "async-trait", "atomic-instant-full", "database", "humansize", @@ -1325,6 +1359,7 @@ dependencies = [ "serde_with", "tauri", "throttle_my_fn", + "tokio", "utils", ] @@ -1352,7 +1387,7 @@ dependencies = [ "deranged 0.4.0", "dirs 6.0.0", "download_manager", - "droplet-rs", + "droplet-rs 0.7.3", "dynfmt", "filetime", "futures-core", @@ -1360,7 +1395,7 @@ dependencies = [ "games", "gethostname", "hex 0.4.3", - "http 1.3.1", + "http 1.4.0", "http-serde 2.1.1", "humansize", "known-folders", @@ -1375,7 +1410,7 @@ dependencies = [ "rayon", "regex", "remote", - "reqwest 0.12.24", + "reqwest 0.12.25", "reqwest-middleware 0.4.2", "reqwest-middleware-cache", "reqwest-websocket", @@ -1403,6 +1438,7 @@ dependencies = [ "throttle_my_fn", "tokio", "tokio-util", + "tracing", "umu-wrapper-lib", "url", "urlencoding", @@ -1411,6 +1447,7 @@ dependencies = [ "walkdir", "webbrowser", "whoami", + "wry", "zstd", ] @@ -1429,6 +1466,29 @@ dependencies = [ "x509-parser 0.17.0", ] +[[package]] +name = "droplet-rs" +version = "0.12.3" +source = "git+https://github.com/Drop-OSS/droplet-rs#fe2b569c06d6dd478ffbc2a72a0ba4cf52adf346" +dependencies = [ + "anyhow", + "async-trait", + "dyn-clone", + "futures", + "getrandom 0.3.4", + "hex 0.4.3", + "humansize", + "rcgen", + "ring", + "serde", + "serde_json", + "sha2 0.10.9", + "time", + "tokio", + "uuid", + "x509-parser 0.17.0", +] + [[package]] name = "dtoa" version = "1.0.10" @@ -1507,9 +1567,9 @@ dependencies = [ [[package]] name = "endi" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" [[package]] name = "enumflags2" @@ -1529,7 +1589,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1549,9 +1609,9 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" dependencies = [ "serde", "serde_core", @@ -1640,15 +1700,15 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -1687,7 +1747,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1790,7 +1850,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1836,25 +1896,33 @@ dependencies = [ name = "games" version = "0.1.0" dependencies = [ + "aes", + "async-scoped", + "async-trait", "atomic-instant-full", "bitcode", "boxcar", + "ctr", "database", "download_manager", + "droplet-rs 0.12.3", + "futures-util", "hex 0.4.3", "log", - "md5 0.8.0", "native_model", "rayon", "remote", - "reqwest 0.12.24", + "reqwest 0.12.25", "rustix 1.1.2", "serde", "serde_json", "serde_with", + "sha2 0.10.9", "sysinfo 0.37.2", "tauri", "throttle_my_fn", + "tokio", + "tokio-util", "utils", ] @@ -1968,9 +2036,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2058,9 +2126,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.8" +version = "0.30.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460" +checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" [[package]] name = "glib" @@ -2068,7 +2136,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "futures-channel", "futures-core", "futures-executor", @@ -2096,7 +2164,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2187,7 +2255,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2202,7 +2270,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.4", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -2220,8 +2288,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.11.4", + "http 1.4.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -2242,9 +2310,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -2301,12 +2369,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2328,7 +2395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -2339,7 +2406,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -2379,7 +2446,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" dependencies = [ - "http 1.3.1", + "http 1.4.0", "serde", ] @@ -2436,16 +2503,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "httparse", "itoa", @@ -2462,8 +2529,8 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.7.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", "rustls", "rustls-native-certs", @@ -2482,7 +2549,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -2492,18 +2559,18 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.7.0", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", @@ -2513,7 +2580,7 @@ dependencies = [ "tokio", "tower-service", "tracing", - "windows-registry", + "windows-registry 0.6.1", ] [[package]] @@ -2552,9 +2619,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2565,9 +2632,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2578,11 +2645,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2593,42 +2659,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -2676,12 +2738,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -2695,6 +2757,15 @@ dependencies = [ "cfb", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "instant" version = "0.1.13" @@ -2712,9 +2783,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -2802,9 +2873,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2838,7 +2909,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "serde", "unicode-segmentation", ] @@ -2860,7 +2931,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 2.11.4", + "indexmap 2.12.1", "selectors", ] @@ -2905,9 +2976,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -2941,7 +3012,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", "redox_syscall 0.5.18", ] @@ -2960,9 +3031,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" @@ -2975,11 +3046,11 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" dependencies = [ - "serde", + "serde_core", "value-bag", ] @@ -2998,7 +3069,7 @@ dependencies = [ "anyhow", "arc-swap", "chrono", - "derive_more 2.0.1", + "derive_more 2.1.0", "fnv", "humantime", "libc", @@ -3061,7 +3132,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3136,7 +3207,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3173,13 +3244,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3198,10 +3269,10 @@ dependencies = [ "dpi", "gtk", "keyboard-types", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "png", "serde", @@ -3250,7 +3321,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3259,7 +3330,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "jni-sys", "log", "ndk-sys", @@ -3295,7 +3366,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -3363,9 +3434,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -3373,30 +3444,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - -[[package]] -name = "objc2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" -dependencies = [ - "objc-sys", - "objc2-encode", + "syn 2.0.111", ] [[package]] @@ -3415,10 +3470,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.10.0", + "block2", "libc", - "objc2 0.6.3", + "objc2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-foundation", @@ -3426,8 +3481,8 @@ dependencies = [ "objc2-core-image", "objc2-core-text", "objc2-core-video", - "objc2-foundation 0.3.2", - "objc2-quartz-core 0.3.2", + "objc2-foundation", + "objc2-quartz-core", ] [[package]] @@ -3436,9 +3491,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "bitflags 2.10.0", + "objc2", + "objc2-foundation", ] [[package]] @@ -3447,9 +3502,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "bitflags 2.10.0", + "objc2", + "objc2-foundation", ] [[package]] @@ -3458,9 +3513,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "dispatch2", - "objc2 0.6.3", + "objc2", ] [[package]] @@ -3469,9 +3524,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "dispatch2", - "objc2 0.6.3", + "objc2", "objc2-core-foundation", "objc2-io-surface", ] @@ -3482,8 +3537,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" dependencies = [ - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", ] [[package]] @@ -3492,8 +3557,8 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", "objc2-core-graphics", ] @@ -3504,8 +3569,8 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", "objc2-core-graphics", "objc2-io-surface", @@ -3526,28 +3591,16 @@ dependencies = [ "cc", ] -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.9.4", - "block2 0.5.1", - "libc", - "objc2 0.5.2", -] - [[package]] name = "objc2-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.10.0", + "block2", "libc", - "objc2 0.6.3", + "objc2", "objc2-core-foundation", ] @@ -3567,8 +3620,8 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", ] @@ -3578,44 +3631,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586" dependencies = [ - "objc2 0.6.3", + "objc2", "objc2-core-foundation", ] -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.9.4", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags 2.9.4", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-metal", -] - [[package]] name = "objc2-quartz-core" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", ] [[package]] @@ -3624,8 +3653,8 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.10.0", + "objc2", "objc2-core-foundation", ] @@ -3635,10 +3664,29 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", ] [[package]] @@ -3647,12 +3695,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", - "objc2 0.6.3", + "bitflags 2.10.0", + "block2", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "objc2-javascript-core", "objc2-security", ] @@ -3695,9 +3743,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "dunce", "is-wsl", @@ -3707,11 +3755,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.74" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -3728,7 +3776,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3739,9 +3787,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.110" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -3786,14 +3834,18 @@ dependencies = [ [[package]] name = "os_info" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +checksum = "7c39b5918402d564846d5aba164c09a66cc88d232179dfd3e3c619a25a268392" dependencies = [ + "android_system_properties", "log", - "plist", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", "serde", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4027,7 +4079,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4057,6 +4109,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.111", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -4093,8 +4165,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.11.4", - "quick-xml 0.38.3", + "indexmap 2.12.1", + "quick-xml 0.38.4", "serde", "time", ] @@ -4128,9 +4200,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -4182,7 +4254,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.7", + "toml_edit 0.23.9", ] [[package]] @@ -4217,9 +4289,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -4254,9 +4326,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] @@ -4318,9 +4390,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -4496,7 +4568,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -4538,7 +4610,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4590,13 +4662,13 @@ dependencies = [ "chrono", "client", "database", - "droplet-rs", + "droplet-rs 0.7.3", "gethostname", "hex 0.4.3", - "http 1.3.1", + "http 1.4.0", "log", "md5 0.8.0", - "reqwest 0.12.24", + "reqwest 0.12.25", "reqwest-websocket", "serde", "serde_with", @@ -4644,9 +4716,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a" dependencies = [ "base64 0.22.1", "bytes", @@ -4655,10 +4727,10 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -4715,8 +4787,8 @@ checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" dependencies = [ "anyhow", "async-trait", - "http 1.3.1", - "reqwest 0.12.24", + "http 1.4.0", + "reqwest 0.12.25", "serde", "thiserror 1.0.69", "tower-service", @@ -4751,7 +4823,7 @@ dependencies = [ "async-tungstenite", "bytes", "futures-util", - "reqwest 0.12.24", + "reqwest 0.12.25", "thiserror 2.0.17", "tokio", "tokio-util", @@ -4767,17 +4839,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ "ashpd", - "block2 0.6.2", + "block2", "dispatch2", "glib-sys", "gobject-sys", "gtk-sys", "js-sys", "log", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", @@ -4873,7 +4945,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4886,7 +4958,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", @@ -4895,9 +4967,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.32" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", "ring", @@ -4921,9 +4993,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ "web-time", "zeroize", @@ -4931,9 +5003,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.7" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -4999,9 +5071,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -5018,7 +5090,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5039,7 +5111,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5052,7 +5124,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5113,7 +5185,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" dependencies = [ - "erased-serde 0.4.8", + "erased-serde 0.4.9", "serde", "serde_core", "typeid", @@ -5146,7 +5218,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5157,7 +5229,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5181,7 +5253,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5216,17 +5288,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex 0.4.3", "indexmap 1.9.3", - "indexmap 2.11.4", + "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.1.0", "serde_core", "serde_json", "serde_with_macros", @@ -5235,14 +5307,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5251,7 +5323,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -5277,7 +5349,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5413,18 +5485,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "siphasher" @@ -5483,24 +5555,24 @@ dependencies = [ [[package]] name = "softbuffer" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ "bytemuck", - "cfg_aliases", - "core-graphics", - "foreign-types 0.5.0", "js-sys", - "log", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", "raw-window-handle", "redox_syscall 0.5.18", + "tracing", "wasm-bindgen", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5634,9 +5706,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -5666,7 +5738,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5723,7 +5795,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -5767,8 +5839,8 @@ version = "0.34.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.10.0", + "block2", "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", @@ -5785,9 +5857,9 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc2 0.6.3", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "parking_lot 0.12.5", "raw-window-handle", @@ -5809,7 +5881,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5840,9 +5912,9 @@ dependencies = [ [[package]] name = "tauri" -version = "2.9.3" +version = "2.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8" +checksum = "8a3868da5508446a7cd08956d523ac3edf0a8bc20bf7e4038f9a95c2800d2033" dependencies = [ "anyhow", "bytes", @@ -5854,22 +5926,22 @@ dependencies = [ "glob", "gtk", "heck 0.5.0", - "http 1.3.1", + "http 1.4.0", "http-range", "jni", "libc", "log", "mime", "muda", - "objc2 0.6.3", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", - "reqwest 0.12.24", + "reqwest 0.12.25", "serde", "serde_json", "serde_repr", @@ -5892,9 +5964,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d" +checksum = "17fcb8819fd16463512a12f531d44826ce566f486d7ccd211c9c8cebdaec4e08" dependencies = [ "anyhow", "cargo_toml", @@ -5914,9 +5986,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f" +checksum = "9fa9844cefcf99554a16e0a278156ae73b0d8680bbc0e2ad1e4287aadd8489cf" dependencies = [ "base64 0.22.1", "brotli", @@ -5930,7 +6002,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "syn 2.0.106", + "syn 2.0.111", "tauri-utils", "thiserror 2.0.17", "time", @@ -5941,23 +6013,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d" +checksum = "3764a12f886d8245e66b7ee9b43ccc47883399be2019a61d80cf0f4117446fde" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.4.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" +checksum = "0e1d0a4860b7ff570c891e1d2a586bf1ede205ff858fbc305e0b5ae5d14c1377" dependencies = [ "anyhow", "glob", @@ -5972,9 +6044,9 @@ dependencies = [ [[package]] name = "tauri-plugin-autostart" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062cdcd483d5e3148c9a64dabf8c574e239e2aa1193cf208d95cf89a676f87a5" +checksum = "459383cebc193cdd03d1ba4acc40f2c408a7abce419d64bdcd2d745bc2886f70" dependencies = [ "auto-launch", "serde", @@ -5986,9 +6058,9 @@ dependencies = [ [[package]] name = "tauri-plugin-deep-link" -version = "2.4.3" +version = "2.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd67112fb1131834c2a7398ffcba520dbbf62c17de3b10329acd1a3554b1a9bb" +checksum = "6e82759f7c7d51de3cbde51c04b3f2332de52436ed84541182cd8944b04e9e73" dependencies = [ "dunce", "plist", @@ -6001,15 +6073,15 @@ dependencies = [ "thiserror 2.0.17", "tracing", "url", - "windows-registry", + "windows-registry 0.5.3", "windows-result 0.3.4", ] [[package]] name = "tauri-plugin-dialog" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beee42a4002bc695550599b011728d9dfabf82f767f134754ed6655e434824e" +checksum = "313f8138692ddc4a2127c4c9607d616a46f5c042e77b3722450866da0aad2f19" dependencies = [ "log", "raw-window-handle", @@ -6025,9 +6097,9 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.4.2" +version = "2.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "315784ec4be45e90a987687bae7235e6be3d6e9e350d2b75c16b8a4bf22c1db7" +checksum = "47df422695255ecbe7bac7012440eddaeefd026656171eac9559f5243d3230d9" dependencies = [ "anyhow", "dunce", @@ -6047,14 +6119,14 @@ dependencies = [ [[package]] name = "tauri-plugin-opener" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5" +checksum = "c26b72571d25dee25667940027114e60f569fc3974f8cefbe50c2cbc5fd65e3b" dependencies = [ "dunce", "glob", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "open", "schemars 0.8.22", "serde", @@ -6069,9 +6141,9 @@ dependencies = [ [[package]] name = "tauri-plugin-os" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba" +checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" dependencies = [ "gethostname", "log", @@ -6087,9 +6159,9 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.3.1" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54777d0c0d8add34eea3ced84378619ef5b97996bd967d3038c668feefd21071" +checksum = "c374b6db45f2a8a304f0273a15080d98c70cde86178855fc24653ba657a1144c" dependencies = [ "encoding_rs", "log", @@ -6108,9 +6180,9 @@ dependencies = [ [[package]] name = "tauri-plugin-single-instance" -version = "2.3.4" +version = "2.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb9cac815bf11c4a80fb498666bcdad66d65b89e3ae24669e47806febb76389c" +checksum = "dd707f8c86b4e3004e2c141fa24351f1909ba40ce1b8437e30d5ed5277dd3710" dependencies = [ "serde", "serde_json", @@ -6124,16 +6196,16 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9368f09358496f2229313fccb37682ad116b7f46fa76981efe116994a0628926" +checksum = "87f766fe9f3d1efc4b59b17e7a891ad5ed195fa8d23582abb02e6c9a01137892" dependencies = [ "cookie", "dpi", "gtk", - "http 1.3.1", + "http 1.4.0", "jni", - "objc2 0.6.3", + "objc2", "objc2-ui-kit", "objc2-web-kit", "raw-window-handle", @@ -6149,17 +6221,17 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929f5df216f5c02a9e894554401bcdab6eec3e39ec6a4a7731c7067fc8688a93" +checksum = "187a3f26f681bdf028f796ccf57cf478c1ee422c50128e5a0a6ebeb3f5910065" dependencies = [ "gtk", - "http 1.3.1", + "http 1.4.0", "jni", "log", - "objc2 0.6.3", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "percent-encoding", "raw-window-handle", @@ -6176,9 +6248,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b8bbe426abdbf52d050e52ed693130dbd68375b9ad82a3fb17efb4c8d85673" +checksum = "76a423c51176eb3616ee9b516a9fa67fed5f0e78baaba680e44eb5dd2cc37490" dependencies = [ "anyhow", "brotli", @@ -6187,7 +6259,7 @@ dependencies = [ "dunce", "glob", "html5ever", - "http 1.3.1", + "http 1.4.0", "infer", "json-patch", "kuchikiki", @@ -6214,10 +6286,11 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ + "dunce", "embed-resource", "toml 0.9.8", ] @@ -6272,7 +6345,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6283,7 +6356,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6314,7 +6387,7 @@ version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ - "deranged 0.5.4", + "deranged 0.5.5", "itoa", "num-conv", "powerfmt", @@ -6350,9 +6423,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -6398,7 +6471,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6423,9 +6496,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -6453,13 +6526,13 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "serde_core", "serde_spanned 1.0.3", "toml_datetime 0.7.3", "toml_parser", "toml_writer", - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -6486,7 +6559,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "toml_datetime 0.6.3", "winnow 0.5.40", ] @@ -6497,7 +6570,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.3", @@ -6506,14 +6579,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.1", "toml_datetime 0.7.3", "toml_parser", - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -6522,7 +6595,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -6548,14 +6621,14 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "iri-string", "pin-project-lite", @@ -6578,10 +6651,11 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -6589,44 +6663,44 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] [[package]] name = "tray-icon" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" +checksum = "e3d5572781bee8e3f994d7467084e1b1fd7a93ce66bd480f8156ba89dee55a2b" dependencies = [ "crossbeam-channel", "dirs 6.0.0", "libappindicator", "muda", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "png", "serde", "thiserror 2.0.17", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -6643,7 +6717,7 @@ checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" dependencies = [ "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "rand 0.9.2", @@ -6739,9 +6813,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" @@ -6834,34 +6908,34 @@ dependencies = [ [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", "rand 0.9.2", - "serde", + "serde_core", "uuid-macro-internal", "wasm-bindgen", ] [[package]] name = "uuid-macro-internal" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9384a660318abfbd7f8932c34d67e4d1ec511095f95972ddc01e19d7ba8413f" +checksum = "39d11901c36b3650df7acb0f9ebe624f35b5ac4e1922ecd3c57f444648429594" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "value-bag" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" [[package]] name = "vcpkg" @@ -6871,9 +6945,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "version_check" @@ -6949,9 +7023,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -6960,25 +7034,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -6989,9 +7049,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6999,22 +7059,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -7052,7 +7112,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "rustix 1.1.2", "wayland-backend", "wayland-scanner", @@ -7064,7 +7124,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7094,9 +7154,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -7122,8 +7182,8 @@ dependencies = [ "jni", "log", "ndk-context", - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "objc2", + "objc2-foundation", "url", "web-sys", ] @@ -7184,9 +7244,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -7213,7 +7273,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7275,10 +7335,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "raw-window-handle", "windows-sys 0.59.0", "windows-version", @@ -7383,7 +7443,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7394,7 +7454,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7440,6 +7500,17 @@ dependencies = [ "windows-strings 0.4.2", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -7811,9 +7882,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -7855,18 +7926,18 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wry" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d78ec082b80fa088569a970d043bb3050abaabf4454101d44514ee8d9a8c9f6" +checksum = "728b7d4c8ec8d81cab295e0b5b8a4c263c0d41a785fb8f8c4df284e5411140a2" dependencies = [ "base64 0.22.1", - "block2 0.6.2", + "block2", "cookie", "crossbeam-channel", "dirs 6.0.0", @@ -7875,16 +7946,16 @@ dependencies = [ "gdkx11", "gtk", "html5ever", - "http 1.3.1", + "http 1.4.0", "javascriptcore-rs", "jni", "kuchikiki", "libc", "ndk", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", "once_cell", @@ -7894,6 +7965,7 @@ dependencies = [ "soup3", "tao-macros", "thiserror 2.0.17", + "tracing", "url", "webkit2gtk", "webkit2gtk-sys", @@ -7988,11 +8060,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -8000,13 +8071,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] @@ -8039,7 +8110,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.13", + "winnow 0.7.14", "zbus_macros", "zbus_names", "zvariant", @@ -8054,7 +8125,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "zbus_names", "zvariant", "zvariant_utils", @@ -8068,28 +8139,28 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.13", + "winnow 0.7.14", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -8109,7 +8180,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] @@ -8121,9 +8192,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -8132,9 +8203,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -8143,13 +8214,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -8190,7 +8261,7 @@ dependencies = [ "enumflags2", "serde", "url", - "winnow 0.7.13", + "winnow 0.7.14", "zvariant_derive", "zvariant_utils", ] @@ -8204,7 +8275,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "zvariant_utils", ] @@ -8217,6 +8288,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.106", - "winnow 0.7.13", + "syn 2.0.111", + "winnow 0.7.14", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c165bee7..50c422b8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -5,10 +5,19 @@ description = "The client application for the open-source, self-hosted game dist authors = ["Drop OSS"] edition = "2024" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = [ + "client", + "cloud_saves", + "database", + "download_manager", + "games", + "process", + "remote", + "utils", +] -[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies] -tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] } +resolver = "3" [lib] # The `_lib` suffix may seem redundant but it is necessary @@ -18,104 +27,85 @@ name = "drop_app_lib" crate-type = ["staticlib", "cdylib", "rlib"] rustflags = ["-C", "target-feature=+aes,+sse2"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[build-dependencies] -tauri-build = { version = "2.0.0", features = [] } +[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies] +tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] } [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" -log = "0.4.22" -hex = "0.4.3" -tauri-plugin-dialog = "2" -http = "1.1.0" -urlencoding = "2.1.3" -md5 = "0.7.0" -chrono = "0.4.38" -tauri-plugin-os = "2" -boxcar = "0.2.7" -umu-wrapper-lib = "0.1.0" -tauri-plugin-autostart = "2.0.0" -shared_child = "1.0.1" -serde_with = "3.12.0" -slice-deque = "0.3.0" -throttle_my_fn = "0.2.6" -parking_lot = "0.12.3" atomic-instant-full = "0.1.0" +bitcode = "0.6.6" +boxcar = "0.2.7" +bytes = "1.10.1" cacache = "13.1.0" -http-serde = "2.1.1" -reqwest-middleware = "0.4.0" -reqwest-middleware-cache = "0.1.1" +chrono = "0.4.38" +client = { version = "0.1.0", path = "./client" } #client +database = { path = "./database" } #database deranged = "=0.4.0" +dirs = "6.0.0" +download_manager = { version = "0.1.0", path = "./download_manager" } #download manager droplet-rs = "0.7.3" +filetime = "0.2.25" +futures-core = "0.3.31" +futures-lite = "2.6.0" +games = { version = "0.1.0", path = "./games" } #games gethostname = "1.0.1" -zstd = "0.13.3" -tar = "0.4.44" +hex = "0.4.3" +http = "1.1.0" +http-serde = "2.1.1" +humansize = "2.1.3" +known-folders = "1.2.0" +log = "0.4.22" +md5 = "0.7.0" +native_model = { version = "0.6.4", features = [ + "rmp_serde_1_3", +], git = "https://github.com/Drop-OSS/native_model.git" } +page_size = "0.6.0" +parking_lot = "0.12.3" +process = { path = "./process" } #process rand = "0.9.1" +rayon = "1.10.0" regex = "1.11.1" -tempfile = "3.19.1" +remote = { version = "0.1.0", path = "./remote" } #remote +reqwest-middleware = "0.4.0" +reqwest-middleware-cache = "0.1.1" +reqwest-websocket = "0.5.0" schemars = "0.8.22" +serde_json = "1" +serde_with = "3.12.0" sha1 = "0.10.6" -dirs = "6.0.0" -whoami = "1.6.0" -filetime = "0.2.25" -walkdir = "2.5.0" -known-folders = "1.2.0" -native_model = { version = "0.6.4", features = ["rmp_serde_1_3"], git = "https://github.com/Drop-OSS/native_model.git"} -tauri-plugin-opener = "2.4.0" -bitcode = "0.6.6" -reqwest-websocket = "0.5.0" -futures-lite = "2.6.0" -page_size = "0.6.0" +shared_child = "1.0.1" +slice-deque = "0.3.0" sysinfo = "0.36.1" -humansize = "2.1.3" +tar = "0.4.44" +tauri-plugin-autostart = "*" +tauri-plugin-deep-link = "*" +tauri-plugin-dialog = "*" +tauri-plugin-opener = "*" +tauri-plugin-os = "*" +tauri-plugin-shell = "*" +tempfile = "3.19.1" +throttle_my_fn = "0.2.6" tokio-util = { version = "0.7.16", features = ["io"] } -futures-core = "0.3.31" -bytes = "1.10.1" -# tailscale = { path = "./tailscale" } - - -# Workspaces -client = { version = "0.1.0", path = "./client" } -database = { path = "./database" } -process = { path = "./process" } -remote = { version = "0.1.0", path = "./remote" } -utils = { path = "./utils" } -games = { version = "0.1.0", path = "./games" } -download_manager = { version = "0.1.0", path = "./download_manager" } +umu-wrapper-lib = "0.1.0" +url = "2.5.2" +urlencoding = "2.1.3" +utils = { path = "./utils" } #utils +walkdir = "2.5.0" +webbrowser = "1.0.2" +whoami = "1.6.0" +zstd = "0.13.3" +wry = { version = "*", features = ["tracing"] } +tracing = { version = "0.1.43", features = ["log"] } [dependencies.dynfmt] version = "0.1.5" features = ["curly"] -[dependencies.tauri] -version = "2.9.3" -features = ["protocol-asset", "tray-icon"] - -[dependencies.tokio] -version = "1.40.0" -features = ["rt", "tokio-macros", "signal"] - [dependencies.log4rs] version = "1.3.0" features = ["console_appender", "file_appender"] -[dependencies.rustix] -version = "0.38.37" -features = ["fs"] - -[dependencies.uuid] -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.reqwest] version = "0.12.22" default-features = false @@ -129,26 +119,34 @@ features = [ "stream", ] +[dependencies.rustbreak] +version = "2" +features = ["other_errors"] # You can also use "yaml_enc" or "bin_enc" + +[dependencies.rustix] +version = "0.38.37" +features = ["fs"] + [dependencies.serde] version = "1" features = ["derive", "rc"] +[dependencies.tauri] +version = "2.9.5" +features = ["protocol-asset", "tray-icon", "unstable"] + +[dependencies.tokio] +version = "1.40.0" +features = ["rt", "tokio-macros", "signal"] + +[dependencies.uuid] +version = "1.10.0" +features = ["v4", "fast-rng", "macro-diagnostics"] + +[build-dependencies] +tauri-build = { version = "*", features = [] } + [profile.release] lto = true codegen-units = 1 panic = 'abort' - - -[workspace] -members = [ - "client", - "database", - "process", - "remote", - "utils", - "cloud_saves", - "download_manager", - "games", -] - -resolver = "3" \ No newline at end of file diff --git a/src-tauri/client/Cargo.toml b/src-tauri/client/Cargo.toml index 6e325231..03dd6c3b 100644 --- a/src-tauri/client/Cargo.toml +++ b/src-tauri/client/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] -bitcode = "0.6.7" -database = { version = "0.1.0", path = "../database" } -log = "0.4.28" -serde = { version = "1.0.228", features = ["derive"] } -tauri = "2.8.5" -tauri-plugin-autostart = "2.5.0" +bitcode = "*" +database = { version = "*", path = "../database" } +log = "*" +serde = { version = "*", features = ["derive"] } +tauri = "*" +tauri-plugin-autostart = "*" diff --git a/src-tauri/database/src/models.rs b/src-tauri/database/src/models.rs index 78778862..8d6bb4fc 100644 --- a/src-tauri/database/src/models.rs +++ b/src-tauri/database/src/models.rs @@ -53,7 +53,10 @@ pub mod data { #[native_model(id = 2, version = 1, with = native_model::rmp_serde_1_3::RmpSerde)] pub struct GameVersion { pub game_id: String, - pub version_name: String, + pub version_id: String, + + pub display_name: Option, + pub version_path: String, pub platform: Platform, diff --git a/src-tauri/download_manager/Cargo.toml b/src-tauri/download_manager/Cargo.toml index 56a9e938..f8e6363d 100644 --- a/src-tauri/download_manager/Cargo.toml +++ b/src-tauri/download_manager/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] -atomic-instant-full = "0.1.0" +async-trait = "0.1.89" +atomic-instant-full = "0.1" database = { version = "0.1.0", path = "../database" } humansize = "2.1.3" log = "0.4.28" @@ -12,6 +13,7 @@ parking_lot = "0.12.5" remote = { version = "0.1.0", path = "../remote" } serde = "1.0.228" serde_with = "3.15.0" -tauri = "2.8.5" +tauri = "*" throttle_my_fn = "0.2.6" +tokio = { version = "1.48.0", features = ["sync"] } utils = { version = "0.1.0", path = "../utils" } diff --git a/src-tauri/download_manager/src/download_manager_builder.rs b/src-tauri/download_manager/src/download_manager_builder.rs index 35047731..c413ccff 100644 --- a/src-tauri/download_manager/src/download_manager_builder.rs +++ b/src-tauri/download_manager/src/download_manager_builder.rs @@ -1,15 +1,13 @@ use std::{ collections::HashMap, - sync::{ - Arc, Mutex, - mpsc::{Receiver, Sender, channel}, - }, - thread::{JoinHandle, spawn}, + sync::{Arc, Mutex}, }; use database::DownloadableMetadata; use log::{debug, error, info, warn}; -use tauri::AppHandle; +use tauri::{AppHandle, async_runtime::JoinHandle}; +use tokio::sync::mpsc; +use tokio::sync::mpsc::{Receiver, Sender}; use utils::{app_emit, lock, send}; use crate::{ @@ -83,7 +81,7 @@ pub struct DownloadManagerBuilder { impl DownloadManagerBuilder { pub fn build(app_handle: AppHandle) -> DownloadManager { let queue = Queue::new(); - let (command_sender, command_receiver) = channel(); + let (command_sender, command_receiver) = mpsc::channel(4); let active_progress = Arc::new(Mutex::new(None)); let status = Arc::new(Mutex::new(DownloadManagerStatus::Empty)); @@ -100,7 +98,10 @@ impl DownloadManagerBuilder { active_control_flag: None, }; - let terminator = spawn(|| manager.manage_queue()); + let terminator = tauri::async_runtime::spawn(async move { + let result = manager.manage_queue().await; + info!("download manager exited with result: {:?}", result); + }); DownloadManager::new(terminator, queue, active_progress, command_sender) } @@ -109,48 +110,51 @@ impl DownloadManagerBuilder { *lock!(self.status) = 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; *lock!(self.progress) = None; - let mut download_thread_lock = lock!(self.current_download_thread); - - if let Some(unfinished_thread) = download_thread_lock.take() - && !unfinished_thread.is_finished() - { - unfinished_thread.join().unwrap(); + if let Some(unfinished_thread) = { + let mut download_thread_lock = lock!(self.current_download_thread); + download_thread_lock.take() + } { + let _ = unfinished_thread.await; } - drop(download_thread_lock); } - fn stop_and_wait_current_download(&self) -> bool { + async fn stop_and_wait_current_download(&self) -> bool { self.set_status(DownloadManagerStatus::Paused); if let Some(current_flag) = &self.active_control_flag { current_flag.set(DownloadThreadControlFlag::Stop); } - let mut download_thread_lock = lock!(self.current_download_thread); - if let Some(current_download_thread) = download_thread_lock.take() { - return current_download_thread.join().is_ok(); + if let Some(current_download_thread) = { + let mut download_thread_lock = lock!(self.current_download_thread); + download_thread_lock.take() + } { + return current_download_thread.await.is_ok(); }; true } - fn manage_queue(mut self) -> Result<(), ()> { + async fn manage_queue(mut self) -> Result<(), ()> { loop { - let signal = match self.command_receiver.recv() { - Ok(signal) => signal, - Err(_) => return Err(()), + let signal = match self.command_receiver.recv().await { + Some(signal) => signal, + None => return Err(()), }; match signal { @@ -161,13 +165,13 @@ impl DownloadManagerBuilder { self.manage_stop_signal(); } 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(); @@ -176,16 +180,16 @@ impl DownloadManagerBuilder { 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(); @@ -249,9 +253,9 @@ impl DownloadManagerBuilder { let mut download_thread_lock = lock!(self.current_download_thread); let app_handle = self.app_handle.clone(); - *download_thread_lock = Some(spawn(move || { + *download_thread_lock = Some(tauri::async_runtime::spawn(async move { loop { - let download_result = match download_agent.download(&app_handle) { + let download_result = match download_agent.download(&app_handle).await { // Ok(true) is for completed and exited properly Ok(v) => v, Err(e) => { @@ -291,7 +295,7 @@ impl DownloadManagerBuilder { } if validate_result { - download_agent.on_complete(&app_handle); + download_agent.on_complete(&app_handle).await; send!( sender, DownloadManagerSignal::Completed(download_agent.metadata()) @@ -314,31 +318,31 @@ impl DownloadManagerBuilder { 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.download_queue.read().front() && interface == &meta { - self.remove_and_cleanup_front_download(&meta); + self.remove_and_cleanup_front_download(&meta).await; } self.push_ui_queue_update(); send!(self.sender, DownloadManagerSignal::Go); } - fn manage_error_signal(&mut self, error: ApplicationDownloadError) { + async fn manage_error_signal(&mut self, error: ApplicationDownloadError) { debug!("got signal Error"); if let Some(metadata) = self.download_queue.read().front() && let Some(current_agent) = self.download_agent_registry.get(metadata) { current_agent.on_error(&self.app_handle, &error); - self.stop_and_wait_current_download(); - self.remove_and_cleanup_front_download(metadata); + self.stop_and_wait_current_download().await; + self.remove_and_cleanup_front_download(metadata).await; } self.push_ui_queue_update(); self.set_status(DownloadManagerStatus::Error); } - fn manage_cancel_signal(&mut self, meta: &DownloadableMetadata) { + async fn manage_cancel_signal(&mut self, meta: &DownloadableMetadata) { debug!("got signal Cancel"); // If the current download is the one we're tryna cancel @@ -348,11 +352,11 @@ impl DownloadManagerBuilder { { self.set_status(DownloadManagerStatus::Paused); current_download.on_cancelled(&self.app_handle); - self.stop_and_wait_current_download(); + self.stop_and_wait_current_download().await; self.download_queue.pop_front(); - self.cleanup_current_download(); + self.cleanup_current_download().await; self.download_agent_registry.remove(meta); debug!("current download queue: {:?}", self.download_queue.read()); } @@ -370,7 +374,7 @@ impl DownloadManagerBuilder { ); } } - self.sender.send(DownloadManagerSignal::Go).unwrap(); + self.sender.send(DownloadManagerSignal::Go).await.unwrap(); self.push_ui_queue_update(); } fn push_ui_stats_update(&self, kbs: usize, time: usize) { diff --git a/src-tauri/download_manager/src/download_manager_frontend.rs b/src-tauri/download_manager/src/download_manager_frontend.rs index fe50c55f..33471d93 100644 --- a/src-tauri/download_manager/src/download_manager_frontend.rs +++ b/src-tauri/download_manager/src/download_manager_frontend.rs @@ -2,16 +2,15 @@ use std::{ any::Any, collections::VecDeque, fmt::Debug, - sync::{ - Mutex, MutexGuard, - mpsc::{SendError, Sender}, - }, - thread::JoinHandle, + sync::{Mutex, MutexGuard}, }; use database::DownloadableMetadata; use log::{debug, info}; use serde::Serialize; +use tauri::async_runtime::JoinHandle; +use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::{self, error::SendError}; use utils::{lock, send}; use crate::error::ApplicationDownloadError; @@ -81,7 +80,7 @@ pub enum DownloadStatus { /// THIS EDITING IS BLOCKING!!! #[derive(Debug)] pub struct DownloadManager { - terminator: Mutex>>>, + terminator: Mutex>>, download_queue: Queue, progress: CurrentProgressObject, command_sender: Sender, @@ -90,7 +89,7 @@ pub struct DownloadManager { #[allow(dead_code)] impl DownloadManager { pub fn new( - terminator: JoinHandle>, + terminator: JoinHandle<()>, download_queue: Queue, progress: CurrentProgressObject, command_sender: Sender, @@ -103,14 +102,15 @@ impl DownloadManager { } } - pub fn queue_download( + pub async fn queue_download( &self, download: DownloadAgent, ) -> Result<(), SendError> { info!("creating download with meta {:?}", download.metadata()); self.command_sender - .send(DownloadManagerSignal::Queue(download))?; - self.command_sender.send(DownloadManagerSignal::Go) + .send(DownloadManagerSignal::Queue(download)) + .await?; + self.command_sender.send(DownloadManagerSignal::Go).await } pub fn edit(&self) -> MutexGuard<'_, VecDeque> { self.download_queue.edit() @@ -122,7 +122,7 @@ impl DownloadManager { let progress_object = (*lock!(self.progress)).clone()?; Some(progress_object.get_progress()) } - pub fn rearrange_string(&self, meta: &DownloadableMetadata, new_index: usize) { + pub async fn rearrange_string(&self, meta: &DownloadableMetadata, new_index: usize) { let mut queue = self.edit(); let current_index = get_index_from_id(&mut queue, meta).expect("Failed to get meta index from id"); @@ -132,10 +132,10 @@ impl DownloadManager { queue.insert(new_index, to_move); send!(self.command_sender, DownloadManagerSignal::UpdateUIQueue); } - pub fn cancel(&self, meta: DownloadableMetadata) { + pub async fn cancel(&self, meta: DownloadableMetadata) { send!(self.command_sender, DownloadManagerSignal::Cancel(meta)); } - pub fn rearrange(&self, current_index: usize, new_index: usize) { + pub async fn rearrange(&self, current_index: usize, new_index: usize) { if current_index == new_index { return; } @@ -147,10 +147,11 @@ impl DownloadManager { debug!("moving download at index {current_index} to index {new_index}"); - let mut queue = self.edit(); - let to_move = queue.remove(current_index).expect("Failed to get"); - queue.insert(new_index, to_move); - drop(queue); + { + let mut queue = self.edit(); + let to_move = queue.remove(current_index).expect("Failed to get"); + queue.insert(new_index, to_move); + } if needs_pause { send!(self.command_sender, DownloadManagerSignal::Go); @@ -158,16 +159,16 @@ impl DownloadManager { send!(self.command_sender, DownloadManagerSignal::UpdateUIQueue); send!(self.command_sender, DownloadManagerSignal::Go); } - pub fn pause_downloads(&self) { + pub async fn pause_downloads(&self) { send!(self.command_sender, DownloadManagerSignal::Stop); } - pub fn resume_downloads(&self) { + pub async fn resume_downloads(&self) { send!(self.command_sender, DownloadManagerSignal::Go); } - pub fn ensure_terminated(&self) -> Result, Box> { + pub async fn ensure_terminated(&self) -> Result<(), tauri::Error> { send!(self.command_sender, DownloadManagerSignal::Finish); let terminator = lock!(self.terminator).take(); - terminator.unwrap().join() + terminator.unwrap().await } pub fn get_sender(&self) -> Sender { self.command_sender.clone() diff --git a/src-tauri/download_manager/src/downloadable.rs b/src-tauri/download_manager/src/downloadable.rs index 0740ff3c..de5104bd 100644 --- a/src-tauri/download_manager/src/downloadable.rs +++ b/src-tauri/download_manager/src/downloadable.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use async_trait::async_trait; use database::DownloadableMetadata; use tauri::AppHandle; @@ -16,8 +17,9 @@ use super::{ * * But the download manager manages the queue state */ +#[async_trait] pub trait Downloadable: Send + Sync { - fn download(&self, app_handle: &AppHandle) -> Result; + async fn download(&self, app_handle: &AppHandle) -> Result; fn validate(&self, app_handle: &AppHandle) -> Result; fn progress(&self) -> Arc; @@ -26,6 +28,6 @@ pub trait Downloadable: Send + Sync { fn metadata(&self) -> DownloadableMetadata; fn on_queued(&self, app_handle: &AppHandle); fn on_error(&self, app_handle: &AppHandle, error: &ApplicationDownloadError); - fn on_complete(&self, app_handle: &AppHandle); + async fn on_complete(&self, app_handle: &AppHandle); fn on_cancelled(&self, app_handle: &AppHandle); } diff --git a/src-tauri/download_manager/src/util/progress_object.rs b/src-tauri/download_manager/src/util/progress_object.rs index 9f61c5d3..fae7dca7 100644 --- a/src-tauri/download_manager/src/util/progress_object.rs +++ b/src-tauri/download_manager/src/util/progress_object.rs @@ -2,7 +2,6 @@ use std::{ sync::{ Arc, Mutex, atomic::{AtomicUsize, Ordering}, - mpsc::Sender, }, time::{Duration, Instant}, }; @@ -10,6 +9,7 @@ use std::{ use atomic_instant_full::AtomicInstant; use throttle_my_fn::throttle; use utils::{lock, send}; +use tokio::sync::mpsc::Sender; use crate::download_manager_frontend::DownloadManagerSignal; @@ -46,7 +46,7 @@ impl ProgressHandle { pub fn add(&self, amount: usize) { self.progress .fetch_add(amount, std::sync::atomic::Ordering::AcqRel); - calculate_update(&self.progress_object); + tauri::async_runtime::spawn(calculate_update(self.progress_object.clone())); } pub fn skip(&self, amount: usize) { self.progress @@ -112,14 +112,17 @@ impl ProgressObject { } } -#[throttle(1, Duration::from_millis(20))] -pub fn calculate_update(progress: &ProgressObject) { +pub async fn calculate_update(progress: Arc) { let last_update_time = progress .last_update_time - .swap(Instant::now(), Ordering::SeqCst); + .load(Ordering::SeqCst); let time_since_last_update = Instant::now() .duration_since(last_update_time) .as_millis_f64(); + if time_since_last_update < 250.0 { + return; + } + progress.last_update_time.swap(Instant::now(), Ordering::SeqCst); let current_bytes_downloaded = progress.sum(); let max = progress.get_max(); @@ -135,25 +138,24 @@ pub fn calculate_update(progress: &ProgressObject) { let bytes_remaining = max.saturating_sub(current_bytes_downloaded); // bytes progress.update_window(kilobytes_per_second as usize); - push_update(progress, bytes_remaining); + push_update(&progress, bytes_remaining).await; } -#[throttle(1, Duration::from_millis(250))] -pub fn push_update(progress: &ProgressObject, bytes_remaining: usize) { +pub async fn push_update(progress: &ProgressObject, bytes_remaining: usize) { let average_speed = progress.rolling.get_average(); let time_remaining = (bytes_remaining / 1000) / average_speed.max(1); - update_ui(progress, average_speed, time_remaining); - update_queue(progress); + update_ui(progress, average_speed, time_remaining).await; + update_queue(progress).await; } -fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time_remaining: usize) { +async fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time_remaining: usize) { send!( progress_object.sender, DownloadManagerSignal::UpdateUIStats(kilobytes_per_second, time_remaining) ); } -fn update_queue(progress: &ProgressObject) { +async fn update_queue(progress: &ProgressObject) { send!(progress.sender, DownloadManagerSignal::UpdateUIQueue) } diff --git a/src-tauri/games/Cargo.toml b/src-tauri/games/Cargo.toml index c50507b9..51d737a8 100644 --- a/src-tauri/games/Cargo.toml +++ b/src-tauri/games/Cargo.toml @@ -11,7 +11,6 @@ database = { version = "0.1.0", path = "../database" } download_manager = { version = "0.1.0", path = "../download_manager" } hex = "0.4.3" log = "0.4.28" -md5 = "0.8.0" rayon = "1.11.0" remote = { version = "0.1.0", path = "../remote" } reqwest = "0.12.23" @@ -19,8 +18,17 @@ rustix = "1.1.2" serde = { version = "1.0.228", features = ["derive"] } serde_with = "3.15.0" sysinfo = "0.37.2" -tauri = "2.8.5" +tauri = "*" throttle_my_fn = "0.2.6" utils = { version = "0.1.0", path = "../utils" } native_model = { version = "0.6.4", features = ["rmp_serde_1_3"], git = "https://github.com/Drop-OSS/native_model.git"} serde_json = "1.0.145" +droplet-rs = { git="https://github.com/Drop-OSS/droplet-rs" } +async-scoped = { version = "0.9.0", features = ["use-tokio"] } +tokio = { version = "*", features = ["rt", "sync"] } +tokio-util = { version = "*", features = ["io"] } +futures-util = "*" +sha2 = "0.10.9" +ctr = "0.9.2" +aes = "0.8.4" +async-trait = "0.1.89" diff --git a/src-tauri/games/src/downloads/download_agent.rs b/src-tauri/games/src/downloads/download_agent.rs index 1d54ca0e..ea3f3b8b 100644 --- a/src-tauri/games/src/downloads/download_agent.rs +++ b/src-tauri/games/src/downloads/download_agent.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use database::{ ApplicationTransientStatus, DownloadType, DownloadableMetadata, borrow_db_checked, borrow_db_mut_checked, @@ -9,34 +10,29 @@ use download_manager::util::download_thread_control_flag::{ DownloadThreadControl, DownloadThreadControlFlag, }; use download_manager::util::progress_object::{ProgressHandle, ProgressObject}; +use droplet_rs::manifest::Manifest; +use futures_util::StreamExt; +use futures_util::stream::FuturesUnordered; use log::{debug, error, info, warn}; -use rayon::ThreadPoolBuilder; use remote::auth::generate_authorization_header; use remote::error::RemoteAccessError; use remote::requests::generate_url; -use remote::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC}; -use std::collections::{HashMap, HashSet}; -use std::fs::{OpenOptions, create_dir_all}; -use std::io; +use remote::utils::DROP_CLIENT_ASYNC; +use std::collections::HashMap; use std::path::{Path, PathBuf}; -use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::time::Instant; +use std::{io, mem}; use tauri::AppHandle; +use tokio::sync::Semaphore; +use tokio::sync::mpsc::Sender; use utils::{app_emit, lock, send}; -#[cfg(target_os = "linux")] -use rustix::fs::{FallocateFlags, fallocate}; - -use crate::downloads::manifest::{ - DownloadBucket, DownloadContext, DownloadDrop, DropManifest, DropValidateContext, ManifestBody, -}; use crate::downloads::utils::get_disk_available; -use crate::downloads::validate::validate_game_chunk; use crate::library::{on_game_complete, push_game_update, set_partially_installed}; use crate::state::GameStatusManager; -use super::download_logic::download_game_bucket; +use super::download_logic::download_game_chunk; use super::drop_data::DropData; static RETRY_COUNT: usize = 3; @@ -48,9 +44,8 @@ pub struct GameDownloadAgent { pub id: String, pub version: String, pub control_flag: DownloadThreadControl, - buckets: Mutex>, context_map: Mutex>, - pub manifest: Mutex>, + pub manifest: Mutex>, pub progress: Arc, sender: Sender, pub dropdata: DropData, @@ -87,14 +82,11 @@ impl GameDownloadAgent { let stored_manifest = DropData::generate(id.clone(), version.clone(), data_base_dir_path.clone()); - let context_lock = stored_manifest.contexts.lock().unwrap().clone(); - let result = Self { id, version, control_flag, manifest: Mutex::new(None), - buckets: Mutex::new(Vec::new()), context_map: Mutex::new(HashMap::new()), progress: Arc::new(ProgressObject::new(0, 0, sender.clone())), sender, @@ -104,19 +96,7 @@ impl GameDownloadAgent { result.ensure_manifest_exists().await?; - let required_space = lock!(result.manifest) - .as_ref() - .unwrap() - .values() - .map(|e| { - e.lengths - .iter() - .enumerate() - .filter(|(i, _)| *context_lock.get(&e.checksums[*i]).unwrap_or(&false)) - .map(|(_, v)| v) - .sum::() - }) - .sum::() as u64; + let required_space = lock!(result.manifest).as_ref().unwrap().size; let available_space = get_disk_available(data_base_dir_path)? as u64; @@ -147,21 +127,22 @@ impl GameDownloadAgent { return Err(ApplicationDownloadError::NotInitialized); } - self.ensure_buckets()?; - self.control_flag.set(DownloadThreadControlFlag::Go); Ok(()) } // Blocking - pub fn download(&self, app_handle: &AppHandle) -> Result { + pub async fn download(&self, app_handle: &AppHandle) -> Result { self.setup_download(app_handle)?; let timer = Instant::now(); info!("beginning download for {}...", self.metadata().id); - let res = self.run().map_err(ApplicationDownloadError::Communication); + let res = self + .run() + .await + .map_err(ApplicationDownloadError::Communication); debug!( "{} took {}ms to download", @@ -207,7 +188,7 @@ impl GameDownloadAgent { )); } - let manifest_download: DropManifest = response + let manifest_download: Manifest = response .json() .await .map_err(|e| ApplicationDownloadError::Communication(e.into()))?; @@ -222,321 +203,119 @@ impl GameDownloadAgent { // Sets it up for both download and validate fn setup_progress(&self) { - let buckets = lock!(self.buckets); - - let chunk_count = buckets.iter().map(|e| e.drops.len()).sum(); - - let total_length = buckets - .iter() - .map(|bucket| bucket.drops.iter().map(|e| e.length).sum::()) - .sum(); + let manifest = lock!(self.manifest); + let manifest = manifest.as_ref().unwrap(); - self.progress.set_max(total_length); - self.progress.set_size(chunk_count); + self.progress.set_max(manifest.size.try_into().unwrap()); + self.progress.set_size(manifest.chunks.len()); self.progress.reset(); } - pub fn ensure_buckets(&self) -> Result<(), ApplicationDownloadError> { - if lock!(self.buckets).is_empty() { - self.generate_buckets()?; - } - - *lock!(self.context_map) = self.dropdata.get_contexts(); - - Ok(()) - } - - pub fn generate_buckets(&self) -> Result<(), ApplicationDownloadError> { - let manifest = lock!(self.manifest) - .clone() - .ok_or(ApplicationDownloadError::NotInitialized)?; - let game_id = self.id.clone(); - - let base_path = Path::new(&self.dropdata.base_path); - create_dir_all(base_path)?; - - let mut buckets = Vec::new(); - - let mut current_buckets = HashMap::::new(); - let mut current_bucket_sizes = HashMap::::new(); - - for (raw_path, chunk) in manifest { - let path = base_path.join(Path::new(&raw_path)); - - let container = path - .parent() - .ok_or(ApplicationDownloadError::IoError(Arc::new(io::Error::new( - io::ErrorKind::NotFound, - "no parent directory", - ))))?; - create_dir_all(container)?; - - let already_exists = path.exists(); - let file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(false) - .open(&path)?; - let mut file_running_offset = 0; - - for (index, length) in chunk.lengths.iter().enumerate() { - let drop = DownloadDrop { - filename: raw_path.to_string(), - start: file_running_offset, - length: *length, - checksum: chunk.checksums[index].clone(), - permissions: chunk.permissions, - path: path.clone(), - index, - }; - file_running_offset += *length; - - if *length >= TARGET_BUCKET_SIZE { - // They get their own bucket - - buckets.push(DownloadBucket { - game_id: game_id.clone(), - version: chunk.version_name.clone(), - drops: vec![drop], - }); - - continue; - } - - let current_bucket_size = current_bucket_sizes - .entry(chunk.version_name.clone()) - .or_insert_with(|| 0); - let c_version_name = chunk.version_name.clone(); - let c_game_id = game_id.clone(); - let current_bucket = current_buckets - .entry(chunk.version_name.clone()) - .or_insert_with(|| DownloadBucket { - game_id: c_game_id, - version: c_version_name, - drops: vec![], - }); - - if (*current_bucket_size + length >= TARGET_BUCKET_SIZE - || current_bucket.drops.len() >= MAX_FILES_PER_BUCKET) - && !current_bucket.drops.is_empty() - { - // Move current bucket into list and make a new one - buckets.push(current_bucket.clone()); - *current_bucket = DownloadBucket { - game_id: game_id.clone(), - version: chunk.version_name.clone(), - drops: vec![], - }; - *current_bucket_size = 0; - } - - current_bucket.drops.push(drop); - *current_bucket_size += *length; - } - - #[cfg(target_os = "linux")] - if file_running_offset > 0 && !already_exists { - let _ = fallocate(file, FallocateFlags::empty(), 0, file_running_offset as u64); - } - } - - for (_, bucket) in current_buckets.into_iter() { - if !bucket.drops.is_empty() { - buckets.push(bucket); - } - } - - info!("buckets: {}", buckets.len()); - - let existing_contexts = self.dropdata.get_contexts(); - self.dropdata.set_contexts( - &buckets - .iter() - .flat_map(|x| x.drops.iter().map(|v| v.checksum.clone())) - .map(|x| { - let contains = existing_contexts.get(&x).unwrap_or(&false); - (x, *contains) - }) - .collect::>(), - ); - - *lock!(self.buckets) = buckets; - - Ok(()) - } - - fn run(&self) -> Result { + async fn run(&self) -> Result { self.setup_progress(); + let (chunks, key) = { + let manifest = lock!(self.manifest); + let manifest = manifest.as_ref().unwrap(); + (manifest.chunks.clone(), manifest.key) + }; + let chunk_len = chunks.len(); + let mut completed_chunks = { + let completed_chunks = lock!(self.context_map); + completed_chunks.clone() + }; let max_download_threads = borrow_db_checked().settings.max_download_threads; - debug!( - "downloading game: {} with {} threads", - self.id, max_download_threads - ); - let pool = ThreadPoolBuilder::new() - .num_threads(max_download_threads) - .build() - .unwrap_or_else(|_| { - panic!("failed to build thread pool with {max_download_threads} threads") - }); - - let buckets = lock!(self.buckets); + let metrics = tauri::async_runtime::handle().inner().metrics(); + info!("using {} workers", metrics.num_workers()); - let mut download_contexts = HashMap::::new(); + let futures_unordered = FuturesUnordered::new(); - let versions = buckets - .iter() - .map(|e| &e.version) - .collect::>() - .into_iter() - .cloned() - .collect::>(); - - info!("downloading across these versions: {versions:?}"); - - let completed_contexts = Arc::new(boxcar::Vec::new()); - let completed_indexes_loop_arc = completed_contexts.clone(); - - for version in versions { - let download_context = DROP_CLIENT_SYNC - .post(generate_url(&["/api/v2/client/context"], &[])?) - .json(&ManifestBody { - game: self.id.clone(), - version: version.clone(), - }) - .header("Authorization", generate_authorization_header()) - .send()?; - - if download_context.status() != 200 { - return Err(RemoteAccessError::InvalidResponse(download_context.json()?)); - } + for (index, (chunk_id, chunk_data)) in chunks.into_iter().enumerate() { + let progress = self.progress.get(index); + let progress_handle = ProgressHandle::new(progress, self.progress.clone()); - let download_context = download_context.json::()?; - info!( - "download context: ({}) {}", - &version, download_context.context - ); - download_contexts.insert(version, download_context); - } + let chunk_length = chunk_data.files.iter().map(|v| v.length).sum(); - let download_contexts = &download_contexts; + if *completed_chunks.get(&chunk_id).unwrap_or(&false) { + progress_handle.skip(chunk_length); + continue; + } - pool.scope(|scope| { - let context_map = lock!(self.context_map); - for (index, bucket) in buckets.iter().enumerate() { - let mut bucket = (*bucket).clone(); - let completed_contexts = completed_indexes_loop_arc.clone(); - - let progress = self.progress.get(index); - let progress_handle = ProgressHandle::new(progress, self.progress.clone()); - - // If we've done this one already, skip it - // Note to future DecDuck, DropData gets loaded into context_map - let todo_drops = bucket - .drops - .into_iter() - .filter(|e| { - let todo = !*context_map.get(&e.checksum).unwrap_or(&false); - if !todo { - progress_handle.skip(e.length); + let sender = self.sender.clone(); + + futures_unordered.push(async move { async move { + for i in 0..RETRY_COUNT { + let loop_progress_handle = progress_handle.clone(); + let base_path = self.dropdata.base_path.clone(); + info!("starting chunk {}", chunk_id); + match download_game_chunk( + &self.id, + &self.version, + &chunk_id, + &key, + &chunk_data, + base_path, + &self.control_flag, + loop_progress_handle, + ) + .await + { + Ok(true) => { + return Some(chunk_id.clone()); } - todo - }) - .collect::>(); - - if todo_drops.is_empty() { - continue; - }; - - bucket.drops = todo_drops; - - let sender = self.sender.clone(); - - let download_context = - download_contexts.get(&bucket.version).unwrap_or_else(|| { - panic!( - "Could not get bucket version {}. Corrupted state.", - bucket.version - ) - }); - - scope.spawn(move |_| { - // 3 attempts - for i in 0..RETRY_COUNT { - let loop_progress_handle = progress_handle.clone(); - match download_game_bucket( - &bucket, - download_context, - &self.control_flag, - loop_progress_handle, - ) { - Ok(true) => { - for drop in bucket.drops { - completed_contexts.push(drop.checksum); - } - return; - } - Ok(false) => return, - Err(e) => { - warn!("game download agent error: {e}"); - - let retry = matches!( - &e, - ApplicationDownloadError::Communication(_) - | ApplicationDownloadError::Checksum - | ApplicationDownloadError::Lock - | ApplicationDownloadError::IoError(_) - ); - - if i == RETRY_COUNT - 1 || !retry { - warn!("retry logic failed, not re-attempting."); - send!(sender, DownloadManagerSignal::Error(e)); - return; - } + Ok(false) => return None, + Err(e) => { + warn!("game download agent error: {e:?}"); + + let retry = matches!( + &e, + ApplicationDownloadError::Communication(_) + | ApplicationDownloadError::Checksum + | ApplicationDownloadError::Lock + | ApplicationDownloadError::IoError(_) + ); + + if i == RETRY_COUNT - 1 || !retry { + warn!("retry logic failed, not re-attempting."); + send!(sender, DownloadManagerSignal::Error(e)); + return None; } } } - }); - } - }); + } + return None; + } }); + } - let newly_completed = completed_contexts.clone(); + let outputs = futures_unordered.buffer_unordered(max_download_threads).collect::>>().await; - let completed_lock_len = { - let mut context_map_lock = lock!(self.context_map); - for (_, item) in newly_completed.iter() { - context_map_lock.insert(item.clone(), true); + for completed_chunk in outputs { + if let Some(completed_chunk) = completed_chunk { + completed_chunks.insert(completed_chunk, true); } + } - context_map_lock.values().filter(|x| **x).count() - }; - - let context_map_lock = lock!(self.context_map); - let contexts = buckets + let drop_data_chunks = completed_chunks .iter() - .flat_map(|x| x.drops.iter().map(|e| e.checksum.clone())) - .map(|x| { - let completed = context_map_lock.get(&x).unwrap_or(&false); - (x, *completed) - }) + .map(|v| (v.0.to_string(), *v.1)) .collect::>(); - drop(context_map_lock); - self.dropdata.set_contexts(&contexts); + self.dropdata.set_contexts(&drop_data_chunks); self.dropdata.write(); // If there are any contexts left which are false - if !contexts.iter().all(|x| x.1) { + if !completed_chunks.iter().all(|x| *x.1) { info!( - "download agent for {} exited without completing ({}/{}) ({} buckets)", + "download agent for {} exited without completing ({}/{})", self.id.clone(), - completed_lock_len, - contexts.len(), - buckets.len() + completed_chunks.iter().filter(|v| *v.1).count(), + chunk_len, ); + self.context_map.replace(completed_chunks).unwrap(); + return Ok(false); } + self.context_map.replace(completed_chunks).unwrap(); Ok(true) } @@ -559,6 +338,7 @@ impl GameDownloadAgent { } pub fn validate(&self, app_handle: &AppHandle) -> Result { + /* self.setup_validate(app_handle); let buckets = lock!(self.buckets); @@ -612,6 +392,7 @@ impl GameDownloadAgent { return Ok(false); } + */ Ok(true) } @@ -628,10 +409,11 @@ impl GameDownloadAgent { } } +#[async_trait] impl Downloadable for GameDownloadAgent { - fn download(&self, app_handle: &AppHandle) -> Result { + async fn download(&self, app_handle: &AppHandle) -> Result { *lock!(self.status) = DownloadStatus::Downloading; - self.download(app_handle) + self.download(app_handle).await } fn validate(&self, app_handle: &AppHandle) -> Result { @@ -688,7 +470,7 @@ impl Downloadable for GameDownloadAgent { ); } - fn on_complete(&self, app_handle: &tauri::AppHandle) { + async fn on_complete(&self, app_handle: &tauri::AppHandle) { match on_game_complete( &self.metadata(), self.dropdata.base_path.to_string_lossy().to_string(), diff --git a/src-tauri/games/src/downloads/download_logic.rs b/src-tauri/games/src/downloads/download_logic.rs index d5d650b5..40f54db8 100644 --- a/src-tauri/games/src/downloads/download_logic.rs +++ b/src-tauri/games/src/downloads/download_logic.rs @@ -1,175 +1,43 @@ use std::fs::{Permissions, set_permissions}; -use std::io::Read; +use std::io::{Read, SeekFrom}; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; +use std::path::PathBuf; use std::sync::Arc; use std::time::Instant; -use std::{ - fs::{File, OpenOptions}, - io::{self, BufWriter, Seek, SeekFrom, Write}, - path::PathBuf, -}; +use aes::cipher::{KeyIvInit, StreamCipher}; use download_manager::error::ApplicationDownloadError; use download_manager::util::download_thread_control_flag::{ DownloadThreadControl, DownloadThreadControlFlag, }; use download_manager::util::progress_object::ProgressHandle; +use droplet_rs::manifest::ChunkData; +use futures_util::StreamExt as _; use log::{debug, info, warn}; -use md5::{Context, Digest}; use remote::auth::generate_authorization_header; use remote::error::{DropServerError, RemoteAccessError}; use remote::requests::generate_url; -use remote::utils::DROP_CLIENT_SYNC; -use reqwest::blocking::Response; - -use crate::downloads::manifest::{ChunkBody, DownloadBucket, DownloadContext, DownloadDrop}; +use remote::utils::DROP_CLIENT_ASYNC; +use sha2::Digest; +use tokio::fs::OpenOptions; +use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; +use tokio_util::io::StreamReader; static MAX_PACKET_LENGTH: usize = 4096 * 4; static BUMP_SIZE: usize = 4096 * 16; -pub struct DropWriter { - hasher: Context, - destination: BufWriter, - progress: ProgressHandle, -} -impl DropWriter { - fn new(path: PathBuf, progress: ProgressHandle) -> Result { - let destination = OpenOptions::new() - .write(true) - .create(true) - .truncate(false) - .open(&path) - .inspect_err(|_v| warn!("failed to open {}", path.display()))?; - Ok(Self { - destination: BufWriter::with_capacity(1024 * 1024, destination), - hasher: Context::new(), - progress, - }) - } - - fn finish(mut self) -> io::Result { - self.flush()?; - Ok(self.hasher.finalize()) - } -} -// Write automatically pushes to file and hasher -impl Write for DropWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.hasher - .write_all(buf) - .map_err(|e| io::Error::other(format!("Unable to write to hasher: {e}")))?; - let bytes_written = self.destination.write(buf)?; - self.progress.add(bytes_written); - - Ok(bytes_written) - } - - fn flush(&mut self) -> io::Result<()> { - self.hasher.flush()?; - self.destination.flush() - } -} -// Seek moves around destination output -impl Seek for DropWriter { - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.destination.seek(pos) - } -} - -pub struct DropDownloadPipeline<'a, R: Read, W: Write> { - pub source: R, - pub drops: Vec, - pub destination: Vec>, - pub control_flag: &'a DownloadThreadControl, - #[allow(dead_code)] - progress: ProgressHandle, -} - -impl<'a> DropDownloadPipeline<'a, Response, File> { - fn new( - source: Response, - drops: Vec, - control_flag: &'a DownloadThreadControl, - progress: ProgressHandle, - ) -> Result { - Ok(Self { - source, - destination: drops - .iter() - .map(|drop| DropWriter::new(drop.path.clone(), progress.clone())) - .try_collect()?, - drops, - control_flag, - progress, - }) - } - - fn copy(&mut self) -> Result { - let mut copy_buffer = [0u8; MAX_PACKET_LENGTH]; - for (index, drop) in self.drops.iter().enumerate() { - let destination = self - .destination - .get_mut(index) - .ok_or(io::Error::other("no destination"))?; - let mut remaining = drop.length; - if drop.start != 0 { - destination.seek(SeekFrom::Start(drop.start as u64))?; - } - let mut last_bump = 0; - loop { - let size = MAX_PACKET_LENGTH.min(remaining); - let size = self - .source - .read(&mut copy_buffer[0..size]) - .inspect_err(|_| { - warn!("got error from {}", drop.filename); - })?; - remaining -= size; - last_bump += size; +const READ_BUF_LEN: usize = 1 * 1024 * 1024; - destination.write_all(©_buffer[0..size])?; +type Aes128Ctr64LE = ctr::Ctr64LE; - if last_bump > BUMP_SIZE { - last_bump -= BUMP_SIZE; - if self.control_flag.get() == DownloadThreadControlFlag::Stop { - return Ok(false); - } - } - - if remaining == 0 { - break; - }; - } - - if self.control_flag.get() == DownloadThreadControlFlag::Stop { - return Ok(false); - } - } - - Ok(true) - } - - #[allow(dead_code)] - fn debug_skip_checksum(self) { - self.destination - .into_iter() - .for_each(|mut e| e.flush().unwrap()); - } - - fn finish(self) -> Result, io::Error> { - let checksums = self - .destination - .into_iter() - .map(|e| e.finish()) - .try_collect()?; - Ok(checksums) - } -} - -pub fn download_game_bucket( - bucket: &DownloadBucket, - ctx: &DownloadContext, +pub async fn download_game_chunk( + game_id: &str, + version_id: &str, + chunk_id: &str, + key: &[u8; 16], + chunk_data: &ChunkData, + base_path: PathBuf, control_flag: &DownloadThreadControl, progress: ProgressHandle, ) -> Result { @@ -183,21 +51,22 @@ pub fn download_game_bucket( let header = generate_authorization_header(); - let url = generate_url(&["/api/v2/client/chunk"], &[]) - .map_err(ApplicationDownloadError::Communication)?; - - let body = ChunkBody::create(ctx, &bucket.drops); + let url = generate_url( + &["/api/v1/depot/content", game_id, version_id, chunk_id], + &[], + ) + .map_err(ApplicationDownloadError::Communication)?; - let response = DROP_CLIENT_SYNC - .post(url) - .json(&body) + let response = DROP_CLIENT_ASYNC + .get(url) .header("Authorization", header) .send() + .await .map_err(|e| ApplicationDownloadError::Communication(e.into()))?; if response.status() != 200 { info!("chunk request got status code: {}", response.status()); - let raw_res = response.text().map_err(|e| { + let raw_res = response.text().await.map_err(|e| { ApplicationDownloadError::Communication(RemoteAccessError::FetchError(e.into())) })?; info!("{raw_res}"); @@ -211,92 +80,62 @@ pub fn download_game_bucket( )); } - let lengths = response - .headers() - .get("Content-Lengths") - .ok_or(ApplicationDownloadError::Communication( - RemoteAccessError::UnparseableResponse("missing Content-Lengths header".to_owned()), - ))? - .to_str() - .map_err(|e| { - ApplicationDownloadError::Communication(RemoteAccessError::UnparseableResponse( - e.to_string(), - )) - })?; - - for (i, raw_length) in lengths.split(",").enumerate() { - let length = raw_length.parse::().unwrap_or(0); - let Some(drop) = bucket.drops.get(i) else { - warn!("invalid number of Content-Lengths recieved: {i}, {lengths}"); - return Err(ApplicationDownloadError::DownloadError( - RemoteAccessError::InvalidResponse(DropServerError { - status_code: 400, - status_message: "Server Error".to_owned(), - message: format!( - "invalid number of Content-Lengths recieved: {i}, {lengths}" - ), - }), - )); - }; - if drop.length != length { - warn!( - "for {}, expected {}, got {} ({})", - drop.filename, drop.length, raw_length, length - ); - return Err(ApplicationDownloadError::DownloadError( - RemoteAccessError::InvalidResponse(DropServerError { - status_code: 400, - status_message: "Server Error".to_owned(), - message: format!( - "for {}, expected {}, got {} ({})", - drop.filename, drop.length, raw_length, length - ), - }), - )); - } - } - let timestep = start.elapsed().as_millis(); debug!("took {}ms to start downloading", timestep); - let mut pipeline = - DropDownloadPipeline::new(response, bucket.drops.clone(), control_flag, progress) - .map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?; - - let completed = pipeline - .copy() - .map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?; - if !completed { - return Ok(false); - } + let stream = response + .bytes_stream() + .map(|v| v.map_err(|err| std::io::Error::other(err))); + let mut stream_reader = StreamReader::new(stream); + + let mut hasher = sha2::Sha256::new(); + let mut cipher = Aes128Ctr64LE::new(key.into(), &chunk_data.iv.into()); + let mut read_buf = vec![0u8; READ_BUF_LEN]; + for file in &chunk_data.files { + let path = base_path.join(file.filename.clone()); + if let Some(parent) = path.parent() { + tokio::fs::create_dir_all(parent).await?; + } + let mut file_handle = OpenOptions::new() + .truncate(false) + .write(true) + .append(false) + .create(true) + .open(&path) + .await?; + file_handle + .seek(SeekFrom::Start(file.start.try_into().unwrap())) + .await?; + + let mut remaining = file.length; + while remaining > 0 { + let amount = stream_reader.read(&mut read_buf[0..remaining.min(READ_BUF_LEN)]).await?; + progress.add(amount); + remaining -= amount; + + cipher.apply_keystream(&mut read_buf[0..amount]); + hasher.update(&read_buf[0..amount]); + file_handle.write_all(&read_buf[0..amount]).await?; + } - // If we complete the file, set the permissions (if on Linux) - #[cfg(unix)] - { - for drop in bucket.drops.iter() { - let permission = if drop.permissions == 0 { + #[cfg(unix)] + { + drop(file_handle); + let permissions = if file.permissions == 0 { 0o744 } else { - drop.permissions + file.permissions }; - let permissions = Permissions::from_mode(permission); - set_permissions(drop.path.clone(), permissions) + let permissions = Permissions::from_mode(permissions); + set_permissions(path, permissions) .map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?; } } - let checksums = pipeline - .finish() - .map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?; - - for (index, drop) in bucket.drops.iter().enumerate() { - let res = hex::encode(**checksums.get(index).unwrap()); - if res != drop.checksum { - warn!("context didn't match... doing nothing because we will validate later."); - // return Ok(false); - // return Err(ApplicationDownloadError::Checksum); - } + let digest = hex::encode(hasher.finalize()); + if digest != chunk_data.checksum { + return Err(ApplicationDownloadError::Checksum); } Ok(true) diff --git a/src-tauri/games/src/downloads/manifest.rs b/src-tauri/games/src/downloads/manifest.rs index b1b4baaf..bba8a9ef 100644 --- a/src-tauri/games/src/downloads/manifest.rs +++ b/src-tauri/games/src/downloads/manifest.rs @@ -61,17 +61,6 @@ impl ChunkBody { } } -pub type DropManifest = HashMap; -#[derive(Serialize, Deserialize, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct DropChunk { - pub permissions: u32, - pub ids: Vec, - pub checksums: Vec, - pub lengths: Vec, - pub version_name: String, -} - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DropValidateContext { pub index: usize, diff --git a/src-tauri/games/src/downloads/mod.rs b/src-tauri/games/src/downloads/mod.rs index 7cd01d0c..afc9d367 100644 --- a/src-tauri/games/src/downloads/mod.rs +++ b/src-tauri/games/src/downloads/mod.rs @@ -3,5 +3,4 @@ mod download_logic; pub mod drop_data; pub mod error; mod manifest; -pub mod utils; -pub mod validate; +pub mod utils; \ No newline at end of file diff --git a/src-tauri/games/src/downloads/validate.rs b/src-tauri/games/src/downloads/validate.rs deleted file mode 100644 index 0c709f7a..00000000 --- a/src-tauri/games/src/downloads/validate.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::{ - fs::File, - io::{self, BufWriter, Read, Seek, SeekFrom, Write}, -}; - -use download_manager::{ - error::ApplicationDownloadError, - util::{ - download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}, - progress_object::ProgressHandle, - }, -}; -use log::debug; -use md5::Context; - -use crate::downloads::manifest::DropValidateContext; - -pub fn validate_game_chunk( - ctx: &DropValidateContext, - control_flag: &DownloadThreadControl, - progress: ProgressHandle, -) -> Result { - debug!( - "Starting chunk validation {}, {}, {} #{}", - ctx.path.display(), - ctx.index, - ctx.offset, - ctx.checksum - ); - // If we're paused - if control_flag.get() == DownloadThreadControlFlag::Stop { - progress.set(0); - return Ok(false); - } - - let Ok(mut source) = File::open(&ctx.path) else { - return Ok(false); - }; - - if ctx.offset != 0 { - source - .seek(SeekFrom::Start(ctx.offset as u64)) - .expect("Failed to seek to file offset"); - } - - let mut hasher = md5::Context::new(); - - let completed = validate_copy(&mut source, &mut hasher, ctx.length, control_flag, progress)?; - if !completed { - return Ok(false); - } - - let res = hex::encode(hasher.finalize().0); - if res != ctx.checksum { - return Ok(false); - } - - debug!( - "Successfully finished verification #{}, copied {} bytes", - ctx.checksum, ctx.length - ); - - Ok(true) -} - -fn validate_copy( - source: &mut File, - dest: &mut Context, - size: usize, - control_flag: &DownloadThreadControl, - progress: ProgressHandle, -) -> Result { - let copy_buf_size = 512; - let mut copy_buf = vec![0; copy_buf_size]; - let mut buf_writer = BufWriter::with_capacity(1024 * 1024, dest); - let mut total_bytes = 0; - - loop { - if control_flag.get() == DownloadThreadControlFlag::Stop { - buf_writer.flush()?; - return Ok(false); - } - - let mut bytes_read = source.read(&mut copy_buf)?; - total_bytes += bytes_read; - - // If we read over (likely), truncate our read to - // the right size - if total_bytes > size { - let over = total_bytes - size; - bytes_read -= over; - total_bytes = size; - } - - buf_writer.write_all(©_buf[0..bytes_read])?; - progress.add(bytes_read); - - if total_bytes >= size { - break; - } - } - buf_writer.flush()?; - Ok(true) -} diff --git a/src-tauri/games/src/lib.rs b/src-tauri/games/src/lib.rs index 7cd794a7..e4e413a7 100644 --- a/src-tauri/games/src/lib.rs +++ b/src-tauri/games/src/lib.rs @@ -1,4 +1,5 @@ #![feature(iterator_try_collect)] +#![feature(lock_value_accessors)] pub mod collections; pub mod downloads; diff --git a/src-tauri/games/src/library.rs b/src-tauri/games/src/library.rs index c58d3c04..0b7f67c2 100644 --- a/src-tauri/games/src/library.rs +++ b/src-tauri/games/src/library.rs @@ -205,11 +205,13 @@ pub fn on_game_complete( let client = DROP_CLIENT_SYNC.clone(); let response = generate_url( - &["/api/v1/client/game/version"], &[ - ("id", &meta.id), - ("version", meta.version.as_ref().unwrap()), + "/api/v1/client/game", + &meta.id, + "version", + meta.version.as_ref().unwrap(), ], + &[], )?; let response = client .get(response) diff --git a/src-tauri/process/Cargo.toml b/src-tauri/process/Cargo.toml index d3d67a1e..e91bacf3 100644 --- a/src-tauri/process/Cargo.toml +++ b/src-tauri/process/Cargo.toml @@ -14,6 +14,6 @@ page_size = "0.6.0" serde = "1.0.228" serde_with = "3.15.0" shared_child = "1.1.1" -tauri = "2.8.5" -tauri-plugin-opener = "2.5.0" +tauri = "*" +tauri-plugin-opener = "*" utils = { version = "0.1.0", path = "../utils" } diff --git a/src-tauri/remote/Cargo.toml b/src-tauri/remote/Cargo.toml index f3523593..2cc86e9f 100644 --- a/src-tauri/remote/Cargo.toml +++ b/src-tauri/remote/Cargo.toml @@ -18,6 +18,6 @@ reqwest = "0.12.23" reqwest-websocket = "0.5.1" serde = "1.0.228" serde_with = "3.15.0" -tauri = "2.8.5" +tauri = "*" url = "2.5.7" utils = { version = "0.1.0", path = "../utils" } diff --git a/src-tauri/remote/src/lib.rs b/src-tauri/remote/src/lib.rs index 3bc0ff65..17f6a04e 100644 --- a/src-tauri/remote/src/lib.rs +++ b/src-tauri/remote/src/lib.rs @@ -1,3 +1,6 @@ +#![feature(slice_as_array)] +#![feature(slice_concat_trait)] + pub mod auth; #[macro_use] pub mod cache; diff --git a/src-tauri/remote/src/requests.rs b/src-tauri/remote/src/requests.rs index df742692..00fde99e 100644 --- a/src-tauri/remote/src/requests.rs +++ b/src-tauri/remote/src/requests.rs @@ -5,14 +5,12 @@ use crate::{ auth::generate_authorization_header, error::RemoteAccessError, utils::DROP_CLIENT_ASYNC, }; -pub fn generate_url>( - path_components: &[T], - query: &[(T, T)], +pub fn generate_url( + path_components: &[&str], + query: &[(&str, &str)], ) -> Result { - let mut base_url = DB.fetch_base_url(); - for endpoint in path_components { - base_url = base_url.join(endpoint.as_ref())?; - } + let path_appended = path_components.join("/"); + let mut base_url = DB.fetch_base_url().join(&path_appended)?; { let mut queries = base_url.query_pairs_mut(); for (param, val) in query { diff --git a/src-tauri/remote/src/server_proto.rs b/src-tauri/remote/src/server_proto.rs index 54334df0..23c1f5bb 100644 --- a/src-tauri/remote/src/server_proto.rs +++ b/src-tauri/remote/src/server_proto.rs @@ -1,12 +1,14 @@ use std::str::FromStr; use database::borrow_db_checked; -use http::{Request, Response, StatusCode, Uri, uri::PathAndQuery}; -use log::{error, warn}; +use http::{ + HeaderMap, HeaderName, HeaderValue, Request, Response, StatusCode, Uri, header::USER_AGENT, + uri::PathAndQuery, +}; +use log::{error, info, warn}; use tauri::UriSchemeResponder; -use utils::webbrowser_open::webbrowser_open; -use crate::utils::DROP_CLIENT_SYNC; +use crate::utils::{DROP_CLIENT_ASYNC, DROP_CLIENT_SYNC}; pub async fn handle_server_proto_offline_wrapper( request: Request>, @@ -27,8 +29,8 @@ pub async fn handle_server_proto_offline( .expect("Failed to build error response for proto offline")) } -pub async fn handle_server_proto_wrapper(request: Request>, responder: UriSchemeResponder) { - match handle_server_proto(request).await { +pub fn handle_server_proto_wrapper(request: Request>, responder: UriSchemeResponder) { + match handle_server_proto(request) { Ok(r) => responder.respond(r), Err(e) => { warn!("Cache error: {e}"); @@ -36,54 +38,63 @@ pub async fn handle_server_proto_wrapper(request: Request>, responder: U Response::builder() .status(e) .body(Vec::new()) + .inspect_err(|v| warn!("{:?}", v)) .expect("Failed to build error response"), ); } } } -async fn handle_server_proto(request: Request>) -> Result>, StatusCode> { - let db_handle = borrow_db_checked(); - let auth = match db_handle.auth.as_ref() { - Some(auth) => auth, - None => { - error!("Could not find auth in database"); - return Err(StatusCode::UNAUTHORIZED); - } - }; - let web_token = match &auth.web_token { - Some(token) => token, - None => return Err(StatusCode::UNAUTHORIZED), +fn handle_server_proto(request: Request>) -> Result>, StatusCode> { + let (remote_uri, web_token) = { + let db_handle = borrow_db_checked(); + let auth = match db_handle.auth.as_ref() { + Some(auth) => auth, + None => { + error!("Could not find auth in database"); + return Err(StatusCode::UNAUTHORIZED); + } + }; + let web_token = match &auth.web_token { + Some(token) => token.clone(), + None => return Err(StatusCode::UNAUTHORIZED), + }; + let remote_uri = db_handle + .base_url + .parse::() + .inspect_err(|v| warn!("{:?}", v)) + .expect("Failed to parse base url"); + (remote_uri, web_token) }; - let remote_uri = db_handle - .base_url - .parse::() - .expect("Failed to parse base url"); let path = request.uri().path(); let mut new_uri = request.uri().clone().into_parts(); new_uri.path_and_query = Some( - PathAndQuery::from_str(&format!("{path}?noWrapper=true")) + PathAndQuery::from_str(path) + .inspect_err(|v| warn!("{:?}", v)) .expect("Failed to parse request path in proto"), ); new_uri.authority = remote_uri.authority().cloned(); new_uri.scheme = remote_uri.scheme().cloned(); let err_msg = &format!("Failed to build new uri from parts {new_uri:?}"); - let new_uri = Uri::from_parts(new_uri).expect(err_msg); - - let whitelist_prefix = ["/store", "/api", "/_", "/fonts"]; + let new_uri = Uri::from_parts(new_uri) + .inspect_err(|v| warn!("{:?}", v)) + .expect(err_msg); - if whitelist_prefix.iter().all(|f| !path.starts_with(f)) { - webbrowser_open(new_uri.to_string()); - return Ok(Response::new(Vec::new())); - } + let mut headers = HeaderMap::new(); + request.headers().clone_into(&mut headers); + headers.remove(USER_AGENT); + headers.append(USER_AGENT, HeaderValue::from_static("Drop Desktop Client")); + headers.append( + "Authorization", + HeaderValue::from_str(&format!("Bearer {web_token}")).unwrap(), + ); let client = DROP_CLIENT_SYNC.clone(); let response = match client .request(request.method().clone(), new_uri.to_string()) - .header("Authorization", format!("Bearer {web_token}")) - .headers(request.headers().clone()) + .headers(headers) .send() { Ok(response) => response, @@ -94,15 +105,26 @@ async fn handle_server_proto(request: Request>) -> Result bytes, Err(e) => return Err(e.status().unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)), }; - let http_response = Response::builder() - .status(response_status) + let client_http_response = client_http_response .body(response_body.to_vec()) + .inspect_err(|v| warn!("{:?}", v)) .expect("Failed to build server proto response"); - Ok(http_response) + Ok(client_http_response) } diff --git a/src-tauri/remote/src/utils.rs b/src-tauri/remote/src/utils.rs index 6ca4e0b2..283cba0a 100644 --- a/src-tauri/remote/src/utils.rs +++ b/src-tauri/remote/src/utils.rs @@ -2,6 +2,7 @@ use std::{ fs::{self, File}, io::Read, sync::LazyLock, + time::Duration, }; use database::db::DATA_ROOT_DIR; @@ -91,6 +92,8 @@ pub fn get_client_sync() -> reqwest::blocking::Client { } client .use_rustls_tls() + .user_agent("Drop Desktop Client") + .connect_timeout(Duration::from_millis(1500)) .build() .expect("Failed to build synchronous client") } @@ -102,6 +105,7 @@ pub fn get_client_async() -> reqwest::Client { } client .use_rustls_tls() + .user_agent("Drop Desktop Client") .build() .expect("Failed to build asynchronous client") } @@ -113,6 +117,7 @@ pub fn get_client_ws() -> reqwest::Client { } client .use_rustls_tls() + .user_agent("Drop Desktop Client") .http1_only() .build() .expect("Failed to build websocket client") diff --git a/src-tauri/src/client.rs b/src-tauri/src/client.rs index 919421d3..d93d8d69 100644 --- a/src-tauri/src/client.rs +++ b/src-tauri/src/client.rs @@ -18,18 +18,15 @@ pub fn fetch_state(state: tauri::State<'_, Mutex>) -> Result match res { - Ok(()) => debug!("download manager terminated correctly"), - Err(()) => error!("download manager failed to terminate correctly"), - }, - Err(e) => panic!("{e:?}"), + match DOWNLOAD_MANAGER.ensure_terminated().await { + Ok(()) => debug!("download manager terminated correctly"), + Err(_) => error!("download manager failed to terminate correctly"), } app.exit(0); @@ -76,7 +73,5 @@ pub fn get_autostart_enabled(app: AppHandle) -> Result Result<(), tauri_plugin_opener::Error> { - app_handle - .opener() - .open_path(path, None::<&str>) + app_handle.opener().open_path(path, None::<&str>) } diff --git a/src-tauri/src/download_manager.rs b/src-tauri/src/download_manager.rs index cfc49c64..00ba02e0 100644 --- a/src-tauri/src/download_manager.rs +++ b/src-tauri/src/download_manager.rs @@ -2,21 +2,21 @@ use database::DownloadableMetadata; use download_manager::DOWNLOAD_MANAGER; #[tauri::command] -pub fn pause_downloads() { - DOWNLOAD_MANAGER.pause_downloads(); +pub async fn pause_downloads() { + DOWNLOAD_MANAGER.pause_downloads().await; } #[tauri::command] -pub fn resume_downloads() { - DOWNLOAD_MANAGER.resume_downloads(); +pub async fn resume_downloads() { + DOWNLOAD_MANAGER.resume_downloads().await; } #[tauri::command] -pub fn move_download_in_queue(old_index: usize, new_index: usize) { - DOWNLOAD_MANAGER.rearrange(old_index, new_index); +pub async fn move_download_in_queue(old_index: usize, new_index: usize) { + DOWNLOAD_MANAGER.rearrange(old_index, new_index).await; } #[tauri::command] -pub fn cancel_game(meta: DownloadableMetadata) { - DOWNLOAD_MANAGER.cancel(meta); +pub async fn cancel_game(meta: DownloadableMetadata) { + DOWNLOAD_MANAGER.cancel(meta).await; } diff --git a/src-tauri/src/downloads.rs b/src-tauri/src/downloads.rs index 7625a676..5886d9af 100644 --- a/src-tauri/src/downloads.rs +++ b/src-tauri/src/downloads.rs @@ -9,14 +9,14 @@ use games::downloads::download_agent::GameDownloadAgent; #[tauri::command] pub async fn download_game( game_id: String, - game_version: String, + version_id: String, install_dir: usize, ) -> Result<(), ApplicationDownloadError> { let sender = { DOWNLOAD_MANAGER.get_sender().clone() }; let game_download_agent = GameDownloadAgent::new_from_index( game_id.clone(), - game_version.clone(), + version_id.clone(), install_dir, sender, ) @@ -27,6 +27,7 @@ pub async fn download_game( DOWNLOAD_MANAGER .queue_download(game_download_agent.clone()) + .await .unwrap(); Ok(()) @@ -71,6 +72,7 @@ pub async fn resume_download(game_id: String) -> Result<(), ApplicationDownloadE DOWNLOAD_MANAGER .queue_download(game_download_agent) + .await .unwrap(); Ok(()) } diff --git a/src-tauri/src/games.rs b/src-tauri/src/games.rs index ba4f2796..e66d96cc 100644 --- a/src-tauri/src/games.rs +++ b/src-tauri/src/games.rs @@ -160,7 +160,8 @@ pub async fn fetch_game_logic( }; let client = DROP_CLIENT_ASYNC.clone(); - let response = generate_url(&["/api/v1/client/game/", &id], &[])?; + let response = generate_url(&["/api/v1/client/game", &id], &[])?; + info!("requesting {}", response); let response = client .get(response) .header("Authorization", generate_authorization_header()) @@ -224,11 +225,6 @@ pub async fn fetch_game_version_options_logic( return Err(RemoteAccessError::InvalidResponse(err)); } - let raw = response.text().await?; - info!("{}", raw); - - return Err(RemoteAccessError::CorruptedState); - let data: Vec = response.json().await?; let state_lock = state.lock(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3230973f..8ebd7d30 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -38,12 +38,14 @@ use log4rs::{ }; use serde::Serialize; use tauri::{ - AppHandle, Manager, RunEvent, WindowEvent, + AppHandle, LogicalPosition, LogicalSize, Manager, RunEvent, WebviewBuilder, WebviewUrl, + WindowBuilder, WindowEvent, menu::{Menu, MenuItem, PredefinedMenuItem}, tray::TrayIconBuilder, }; use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_dialog::DialogExt; +use tracing::{Level, span}; use url::Url; use utils::app_emit; @@ -204,6 +206,8 @@ pub fn custom_panic_handler(e: &PanicHookInfo) -> Option<()> { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + // let global_span = span!(Level::TRACE, "global_span"); + // let _enter = global_span.enter(); std::panic::set_hook(Box::new(|e| { let _ = custom_panic_handler(e); println!("{e}"); @@ -296,6 +300,9 @@ pub fn run() { let handle = app.handle().clone(); + let width = 1536.0; + let height = 864.0; + 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 @@ -303,7 +310,7 @@ pub fn run() { ) .title("Drop Desktop App") .min_inner_size(1000.0, 500.0) - .inner_size(1536.0, 864.0) + .inner_size(width, height) .decorations(false) .shadow(false) .data_directory(DATA_ROOT_DIR.join(".webview")) @@ -368,7 +375,7 @@ pub fn run() { .expect("Failed to show window"); } "quit" => { - cleanup_and_exit(app); + app.exit(0); } _ => { @@ -409,20 +416,8 @@ pub fn run() { }); }) .register_asynchronous_uri_scheme_protocol("server", |ctx, request, responder| { - tauri::async_runtime::block_on(async move { - let state = ctx - .app_handle() - .state::>>(); - - offline!( - state, - handle_server_proto_wrapper, - handle_server_proto_offline_wrapper, - request, - responder - ) - .await; - }); + let scope_holder = ctx.app_handle(); + handle_server_proto_wrapper(request, responder); }) .on_window_event(|window, event| { if let WindowEvent::CloseRequested { api, .. } = event { diff --git a/src-tauri/utils/src/download_manager_send.rs b/src-tauri/utils/src/download_manager_send.rs index dc98429b..8cd7e666 100644 --- a/src-tauri/utils/src/download_manager_send.rs +++ b/src-tauri/utils/src/download_manager_send.rs @@ -1,7 +1,7 @@ #[macro_export] macro_rules! send { ($download_manager:expr, $signal:expr) => { - $download_manager.send($signal).unwrap_or_else(|_| { + $download_manager.send($signal).await.unwrap_or_else(|_| { panic!( "Failed to send signal {} to the download manager", stringify!(signal) From b537f89f82b021d93f4d5ef730cec5931a3f77a5 Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sat, 20 Dec 2025 01:10:04 +1100 Subject: [PATCH 02/19] feat: frontend fixes and experimental webview store --- main/package.json | 2 +- main/pages/library/[id]/index.vue | 17 +++++++++++++---- main/pages/store/index.vue | 31 +++---------------------------- main/pnpm-lock.yaml | 14 +++++++------- 4 files changed, 24 insertions(+), 40 deletions(-) diff --git a/main/package.json b/main/package.json index 15be40f3..b5780dc0 100644 --- a/main/package.json +++ b/main/package.json @@ -13,7 +13,7 @@ "@headlessui/vue": "^1.7.23", "@heroicons/vue": "^2.1.5", "@nuxtjs/tailwindcss": "^6.12.2", - "@tauri-apps/api": "^2.7.0", + "@tauri-apps/api": "^2.9.1", "@tauri-apps/plugin-os": "^2.3.2", "@tauri-apps/plugin-shell": "^2.3.3", "koa": "^2.16.1", diff --git a/main/pages/library/[id]/index.vue b/main/pages/library/[id]/index.vue index 5fc30fc2..40fa360f 100644 --- a/main/pages/library/[id]/index.vue +++ b/main/pages/library/[id]/index.vue @@ -181,7 +181,10 @@ class="relative w-full cursor-default rounded-md bg-zinc-800 py-1.5 pl-3 pr-10 text-left text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-700 focus:outline-none focus:ring-2 focus:ring-blue-600 sm:text-sm/6" > {{ versionOptions[installVersionIndex].versionName }} + >{{ + versionOptions[installVersionIndex].displayName || + versionOptions[installVersionIndex].versionPath + }} on {{ versionOptions[installVersionIndex].platform }} @@ -223,7 +226,7 @@ : 'font-normal', 'block truncate', ]" - >{{ version.versionName }} on + >{{ version.displayName || version.versionPath }} on {{ version.platform }} @@ -521,7 +524,13 @@ const htmlDescription = micromark(game.value.mDescription); const installFlowOpen = ref(false); const versionOptions = ref< - undefined | Array<{ versionName: string; platform: string }> + | undefined + | Array<{ + versionId: string; + displayName?: string; + versionPath: string; + platform: string; + }> >(); const installDirs = ref>(); const currentImageIndex = ref(0); @@ -554,7 +563,7 @@ async function install() { installLoading.value = true; await invoke("download_game", { gameId: game.value.id, - gameVersion: versionOptions.value[installVersionIndex.value].versionName, + versionId: versionOptions.value[installVersionIndex.value].versionId, installDir: installDir.value, }); installFlowOpen.value = false; diff --git a/main/pages/store/index.vue b/main/pages/store/index.vue index 51b3d210..5e26efec 100644 --- a/main/pages/store/index.vue +++ b/main/pages/store/index.vue @@ -1,37 +1,12 @@