diff --git a/desktop/.gitignore b/desktop/.gitignore index 2f4d8228..5767a3f2 100644 --- a/desktop/.gitignore +++ b/desktop/.gitignore @@ -23,4 +23,7 @@ dist-ssr *.sln *.sw? .nuxt -.output \ No newline at end of file +.output + +src-tauri/flamegraph.svg +src-tauri/perf* \ No newline at end of file diff --git a/desktop/DEBUG.md b/desktop/DEBUG.md new file mode 100644 index 00000000..2a4478f3 --- /dev/null +++ b/desktop/DEBUG.md @@ -0,0 +1,15 @@ +# How to create Flamegraph + +Run this in `src-tauri`: +``` +WEBKIT_DISABLE_DMABUF_RENDERER=1 CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --release +``` + +You can leave out `WEBKIT_DISABLE_DMABUF_RENDERER=1` if you're not on NVIDIA/Linux + +And then run this in the root dir: +``` +yarn dev --port 1432 +``` + +And then do what you want, and it'll create the flamegraph for you diff --git a/desktop/README.md b/desktop/README.md index f4b83060..b0df3337 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -6,7 +6,7 @@ Drop app is the companion app for [Drop](https://github.com/Drop-OSS/drop). It u Install dependencies with `yarn` -Run the app in development with `yarn tauri dev`. NVIDIA users on Linux, use the environment variable in `.env` +Run the app in development with `yarn tauri dev`. NVIDIA users on Linux, use shell script `./nvidia-prop-dev.sh` To manually specify the logging level, add the environment variable `RUST_LOG=[debug, info, warn, error]` to `yarn tauri dev`: diff --git a/desktop/app.vue b/desktop/app.vue index 5acff0e3..88fee672 100644 --- a/desktop/app.vue +++ b/desktop/app.vue @@ -5,7 +5,11 @@ diff --git a/desktop/components/Header.vue b/desktop/components/Header.vue index 33ee5062..75537eb1 100644 --- a/desktop/components/Header.vue +++ b/desktop/components/Header.vue @@ -1,53 +1,57 @@ diff --git a/desktop/components/HeaderQueueWidget.vue b/desktop/components/HeaderQueueWidget.vue new file mode 100644 index 00000000..23c5ad5a --- /dev/null +++ b/desktop/components/HeaderQueueWidget.vue @@ -0,0 +1,26 @@ + + + diff --git a/desktop/components/HeaderUserWidget.vue b/desktop/components/HeaderUserWidget.vue index 410ba72d..27f2867d 100644 --- a/desktop/components/HeaderUserWidget.vue +++ b/desktop/components/HeaderUserWidget.vue @@ -21,7 +21,7 @@ leave-to-class="transform opacity-0 scale-95" > Connect to different instance @@ -68,7 +74,15 @@ diff --git a/desktop/pages/library.vue b/desktop/pages/library.vue index dcb0b849..ac7e24f5 100644 --- a/desktop/pages/library.vue +++ b/desktop/pages/library.vue @@ -13,7 +13,7 @@ >
@@ -40,12 +40,10 @@ diff --git a/desktop/pages/queue.vue b/desktop/pages/queue.vue new file mode 100644 index 00000000..fcf44e9f --- /dev/null +++ b/desktop/pages/queue.vue @@ -0,0 +1,24 @@ + + + diff --git a/desktop/pages/settings.vue b/desktop/pages/settings.vue index 35b9783b..1e92a0f6 100644 --- a/desktop/pages/settings.vue +++ b/desktop/pages/settings.vue @@ -13,8 +13,8 @@ :href="item.route" :class="[ itemIdx === currentPageIndex - ? 'bg-zinc-800/50 text-blue-600' - : 'text-zinc-400 hover:bg-zinc-800/30 hover:text-blue-600', + ? 'bg-zinc-800/50 text-zinc-100' + : 'text-zinc-400 hover:bg-zinc-800/30 hover:text-zinc-200', 'transition group flex gap-x-3 rounded-md p-2 pr-12 text-sm font-semibold leading-6', ]" > @@ -22,8 +22,8 @@ :is="item.icon" :class="[ itemIdx === currentPageIndex - ? 'text-blue-600' - : 'text-zinc-400 group-hover:text-blue-600', + ? 'text-zinc-100' + : 'text-zinc-400 group-hover:text-zinc-200', 'transition h-6 w-6 shrink-0', ]" aria-hidden="true" diff --git a/desktop/pages/settings/downloads.vue b/desktop/pages/settings/downloads.vue index 27e0f695..237ea72c 100644 --- a/desktop/pages/settings/downloads.vue +++ b/desktop/pages/settings/downloads.vue @@ -1,3 +1,238 @@ \ No newline at end of file +
+
+
+
+

+ Install directories +

+

+ This is where Drop will download game files to, and store them + indefinitely while you play. Drop and games may store other + information elsewhere, like saves or mods. +

+
+
+ +
+
+
+
    +
  • +
    + +
    +

    + {{ dir }} +

    +
    +
    +
    + +
    +
  • +
+
+ + + +
+ + +
+
+ + +
+
+
+ +
+ +
+

+ Select an empty directory to add. +

+
+
+
+
+ + Upload + + +
+
+
+
+
+
+

+ {{ error }} +

+
+
+
+
+
+
+
+
+
+ + + diff --git a/desktop/pages/store/index.vue b/desktop/pages/store/index.vue index ee5f7b22..696d67cd 100644 --- a/desktop/pages/store/index.vue +++ b/desktop/pages/store/index.vue @@ -7,8 +7,29 @@ @click="startGameDownload" > Download game - ({{ Math.floor(progress * 1000) / 10 }}%) + + ({{ Math.floor(progress * 1000) / 10 }}%) + + + + + diff --git a/desktop/plugins/vuedraggable.ts b/desktop/plugins/vuedraggable.ts new file mode 100644 index 00000000..58d50a0b --- /dev/null +++ b/desktop/plugins/vuedraggable.ts @@ -0,0 +1,5 @@ +import draggable from "vuedraggable"; + +export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.vueApp.component("draggable", draggable); +}); diff --git a/desktop/prisma/schema.prisma b/desktop/prisma/schema.prisma deleted file mode 100644 index 1ca51a1f..00000000 --- a/desktop/prisma/schema.prisma +++ /dev/null @@ -1,150 +0,0 @@ -// This should be copied from the main Drop repo -// TODO: do this automatically - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -model User { - id String @id @default(uuid()) - username String @unique - admin Boolean @default(false) - - email String - displayName String - profilePicture String // Object - - authMecs LinkedAuthMec[] - clients Client[] -} - -enum AuthMec { - Simple -} - -model LinkedAuthMec { - userId String - mec AuthMec - - credentials Json - - user User @relation(fields: [userId], references: [id]) - - @@id([userId, mec]) -} - -enum ClientCapabilities { - DownloadAggregation -} - -enum Platform { - Windows @map("windows") - Linux @map("linux") -} - -// References a device -model Client { - id String @id @default(uuid()) - userId String - user User @relation(fields: [userId], references: [id]) - - endpoint String - capabilities ClientCapabilities[] - - name String - platform Platform - lastConnected DateTime -} - -enum MetadataSource { - Custom - GiantBomb -} - -model Game { - id String @id @default(uuid()) - - metadataSource MetadataSource - metadataId String - - // Any field prefixed with m is filled in from metadata - // Acts as a cache so we can search and filter it - mName String // Name of game - mShortDescription String // Short description - mDescription String // Supports markdown - mDevelopers Developer[] - mPublishers Publisher[] - - mReviewCount Int - mReviewRating Float - - mIconId String // linked to objects in s3 - mBannerId String // linked to objects in s3 - mCoverId String - mImageLibrary String[] // linked to objects in s3 - - versions GameVersion[] - libraryBasePath String @unique // Base dir for all the game versions - - @@unique([metadataSource, metadataId], name: "metadataKey") -} - -// A particular set of files that relate to the version -model GameVersion { - gameId String - game Game @relation(fields: [gameId], references: [id]) - versionName String // Sub directory for the game files - - platform Platform - launchCommand String // Command to run to start. Platform-specific. Windows games on Linux will wrap this command in Proton/Wine - setupCommand String // Command to setup game (dependencies and such) - dropletManifest Json // Results from droplet - - versionIndex Int - delta Boolean @default(false) - - @@id([gameId, versionName]) -} - -model Developer { - id String @id @default(uuid()) - - metadataSource MetadataSource - metadataId String - metadataOriginalQuery String - - mName String - mShortDescription String - mDescription String - mLogo String - mBanner String - mWebsite String - - games Game[] - - @@unique([metadataSource, metadataId, metadataOriginalQuery], name: "metadataKey") -} - -model Publisher { - id String @id @default(uuid()) - - metadataSource MetadataSource - metadataId String - metadataOriginalQuery String - - mName String - mShortDescription String - mDescription String - mLogo String - mBanner String - mWebsite String - - games Game[] - - @@unique([metadataSource, metadataId, metadataOriginalQuery], name: "metadataKey") -} diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index ead261bf..9a75a23b 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arc-swap" @@ -70,9 +70,9 @@ checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "ashpd" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9" +checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3" dependencies = [ "enumflags2", "futures-channel", @@ -86,7 +86,7 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-protocols", - "zbus", + "zbus 5.1.1", ] [[package]] @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -194,7 +194,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -229,7 +229,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -282,12 +282,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.21.7" @@ -300,15 +294,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -363,9 +348,9 @@ dependencies = [ [[package]] name = "brotli" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -390,9 +375,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -402,9 +387,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" dependencies = [ "serde", ] @@ -420,7 +405,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -445,9 +430,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -463,7 +448,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -473,14 +458,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" dependencies = [ "serde", - "toml 0.8.2", + "toml 0.8.19", ] [[package]] name = "cc" -version = "1.1.25" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "shlex", ] @@ -614,6 +599,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -666,9 +661,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -756,17 +751,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -790,7 +785,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -801,7 +796,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -835,7 +830,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -890,13 +885,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "dlib" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading", + "libloading 0.8.6", ] [[package]] @@ -919,7 +925,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1012,14 +1018,14 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "embed-resource" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e24052d7be71f0efb50c201557f6fe7d237cfd5a64fd5bcd7fd8fe32dbbffa" +checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.2", + "toml 0.8.19", "vswhom", "winreg", ] @@ -1032,9 +1038,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1063,7 +1069,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1084,12 +1090,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1105,9 +1111,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener", "pin-project-lite", @@ -1115,15 +1121,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -1140,23 +1146,14 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", ] -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1190,7 +1187,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1259,9 +1256,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand", "futures-core", @@ -1278,7 +1275,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1472,7 +1469,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1508,7 +1505,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1518,11 +1515,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -1601,14 +1598,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1616,7 +1613,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.6.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1637,9 +1634,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -1653,12 +1650,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.4.0" @@ -1696,13 +1687,13 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", - "itoa 1.0.11", + "itoa 1.0.14", ] [[package]] @@ -1742,9 +1733,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -1753,7 +1744,7 @@ dependencies = [ "http", "http-body", "httparse", - "itoa 1.0.11", + "itoa 1.0.14", "pin-project-lite", "smallvec", "tokio", @@ -1795,9 +1786,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -1845,6 +1836,124 @@ dependencies = [ "png", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1853,12 +1962,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1874,12 +1994,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "serde", ] @@ -1934,9 +2054,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "javascriptcore-rs" @@ -1972,7 +2092,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -1985,32 +2105,32 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "json-patch" -version = "2.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ "jsonptr", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "jsonptr" -version = "0.4.7" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" dependencies = [ - "fluent-uri", "serde", "serde_json", ] @@ -2065,15 +2185,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] [[package]] name = "libc" -version = "0.2.159" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libloading" @@ -2085,6 +2205,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libredox" version = "0.1.3" @@ -2101,6 +2231,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -2148,7 +2284,7 @@ dependencies = [ "serde-value", "serde_json", "serde_yaml", - "thiserror", + "thiserror 1.0.69", "thread-id", "typemap-ors", "winapi", @@ -2228,11 +2364,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", @@ -2240,9 +2375,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.15.1" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8123dfd4996055ac9b15a60ad263b44b01e539007523ad7a4a533a3d93b0591" +checksum = "fdae9c00e61cc0579bcac625e8ad22104c60548a025bfc972dc83868a28e1484" dependencies = [ "crossbeam-channel", "dpi", @@ -2254,7 +2389,7 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror", + "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -2287,7 +2422,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2313,12 +2448,13 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.6.0", "cfg-if", + "cfg_aliases", "libc", "memoffset", ] @@ -2359,10 +2495,10 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2372,7 +2508,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", - "objc_exception", ] [[package]] @@ -2380,6 +2515,9 @@ name = "objc-sys" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" +dependencies = [ + "cc", +] [[package]] name = "objc2" @@ -2407,6 +2545,30 @@ dependencies = [ "objc2-quartz-core", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -2431,6 +2593,18 @@ dependencies = [ "objc2-metal", ] +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + [[package]] name = "objc2-encode" version = "4.0.3" @@ -2450,6 +2624,18 @@ dependencies = [ "objc2", ] +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-metal" version = "0.2.2" @@ -2476,21 +2662,71 @@ dependencies = [ ] [[package]] -name = "objc_exception" -version = "0.1.2" +name = "objc2-symbols" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "cc", + "objc2", + "objc2-foundation", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-ui-kit" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "objc", + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] @@ -2504,18 +2740,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "open" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" dependencies = [ "is-wsl", "libc", @@ -2524,9 +2757,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -2545,7 +2778,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2565,9 +2798,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -2677,9 +2910,9 @@ dependencies = [ [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "percent-encoding" @@ -2791,7 +3024,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -2823,9 +3056,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2857,7 +3090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", - "indexmap 2.6.0", + "indexmap 2.7.0", "quick-xml 0.32.0", "serde", "time", @@ -2865,9 +3098,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.14" +version = "0.17.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2878,25 +3111,19 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", "rustix", "tracing", "windows-sys 0.59.0", ] -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - [[package]] name = "powerfmt" version = "0.2.0" @@ -2930,12 +3157,20 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.22", ] [[package]] @@ -2970,9 +3205,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3128,14 +3363,14 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -3145,9 +3380,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -3162,9 +3397,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", @@ -3208,9 +3443,9 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +checksum = "46f6f80a9b882647d9014673ca9925d30ffc9750f2eed2b4490e189eaebd01e8" dependencies = [ "ashpd", "block2", @@ -3261,11 +3496,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460d97902465327d69ecfe8cefdb5972c6f94d6127ac9e992acdb51458bebc27" dependencies = [ - "base64 0.12.3", - "bincode", "serde", "tempfile", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3285,22 +3518,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "once_cell", "rustls-pki-types", @@ -3320,9 +3553,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -3352,9 +3585,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3383,7 +3616,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -3413,9 +3646,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" dependencies = [ "core-foundation-sys", "libc", @@ -3452,9 +3685,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -3482,13 +3715,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -3499,16 +3732,16 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ - "itoa 1.0.11", + "itoa 1.0.14", "memchr", "ryu", "serde", @@ -3522,7 +3755,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -3541,22 +3774,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.11", + "itoa 1.0.14", "ryu", "serde", ] [[package]] name = "serde_with" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9720086b3357bcb44fce40117d769a4d068c70ecfa190850a980a71755f66fcc" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -3566,14 +3799,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f1abbfe725f27678f4663bcacb75a83e829fd464c25d78dd038a3a29e307cec" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -3582,8 +3815,8 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.6.0", - "itoa 1.0.11", + "indexmap 2.7.0", + "itoa 1.0.14", "ryu", "serde", "unsafe-libyaml", @@ -3697,9 +3930,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3833,9 +4066,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3844,13 +4077,24 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -3881,15 +4125,15 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.2", + "toml 0.8.19", "version-compare", ] [[package]] name = "tao" -version = "0.30.3" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0dbbebe82d02044dfa481adca1550d6dd7bd16e086bc34fa0fbecceb5a63751" +checksum = "6682a07cf5bab0b8a2bd20d0a542917ab928b5edb75ebd4eda6b05cbaab872da" dependencies = [ "bitflags 2.6.0", "cocoa", @@ -3932,7 +4176,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -3943,9 +4187,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3fad474c02a3bcd4a304afff97159a31b9bab84e29563f7109c7b0ce8cd774e" +checksum = "e545de0a2dfe296fa67db208266cd397c5a55ae782da77973ef4c4fac90e9f2c" dependencies = [ "anyhow", "bytes", @@ -3980,7 +4224,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror", + "thiserror 2.0.6", "tokio", "tray-icon", "url", @@ -3993,9 +4237,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935f9b3c49b22b3e2e485a57f46d61cd1ae07b1cbb2ba87387a387caf2d8c4e7" +checksum = "7bd2a4bcfaf5fb9f4be72520eefcb61ae565038f8ccba2a497d8c28f463b8c01" dependencies = [ "anyhow", "cargo_toml", @@ -4009,15 +4253,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d7443dd4f0b597704b6a14b964ee2ed16e99928d8e6292ae9825f09fbcd30e" +checksum = "bf79faeecf301d3e969b1fae977039edb77a4c1f25cc0a961be298b54bff97cf" dependencies = [ "base64 0.22.1", "brotli", @@ -4031,9 +4275,9 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.79", + "syn 2.0.90", "tauri-utils", - "thiserror", + "thiserror 2.0.6", "time", "url", "uuid", @@ -4042,23 +4286,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2c0963ccfc3f5194415f2cce7acc975942a8797fbabfb0aa1ed6f59326ae7f" +checksum = "c52027c8c5afb83166dacddc092ee8fff50772f9646d461d8c33ee887e447a03" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e6660a409963e4d57b9bfab4addd141eeff41bd3a7fb14e13004a832cf7ef6" +checksum = "e753f2a30933a9bbf0a202fa47d7cc4a3401f06e8d6dcc53b79aa62954828c79" dependencies = [ "anyhow", "glob", @@ -4067,25 +4311,25 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] [[package]] name = "tauri-plugin-deep-link" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a9b5725027c6e9e075b06cb2d5c2cd3b5c29daa8012b404e1ff755cc56082f" +checksum = "1fb46bb8be2c47e344892f387c7aeb6a203a9edea0001e3017c0518d15520786" dependencies = [ "dunce", - "log", "rust-ini", "serde", "serde_json", "tauri", "tauri-plugin", "tauri-utils", - "thiserror", + "thiserror 2.0.6", + "tracing", "url", "windows-registry 0.3.0", "windows-result", @@ -4093,9 +4337,9 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb2fe88b602461c118722c574e2775ab26a4e68886680583874b2f6520608b7" +checksum = "0962c2b9210e43cd849790d33bdf3d28c0e50d9884493fb340835244a708b5ba" dependencies = [ "log", "raw-window-handle", @@ -4105,15 +4349,15 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror", + "thiserror 2.0.6", "url", ] [[package]] name = "tauri-plugin-fs" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab300488ebec3487ca5f56289692e7e45feb07eea8d5e1dba497f7dc9dd9c407" +checksum = "0cdaf6701ee5efc83161cf41004aa5aec4d255c9fb2d2b11fe518fe506acc588" dependencies = [ "anyhow", "dunce", @@ -4125,16 +4369,18 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "thiserror", + "tauri-utils", + "thiserror 2.0.6", + "toml 0.8.19", "url", "uuid", ] [[package]] name = "tauri-plugin-shell" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "371fb9aca2823990a2d0db7970573be5fdf07881fcaa2b835b29631feb84aec1" +checksum = "0ad7880c5586b6b2104be451e3d7fc0f3800c84bda69e9ba81c828f87cb34267" dependencies = [ "encoding_rs", "log", @@ -4147,31 +4393,31 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror", + "thiserror 1.0.69", "tokio", ] [[package]] name = "tauri-plugin-single-instance" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25ac834491d089699a2bc9266a662faf373c9f779f05a2235bc6e4d9e61769a" +checksum = "494d348f82cfa766df4d2ebd96fc38b9cada6d120f13a36fd11395180213ec74" dependencies = [ - "log", "serde", "serde_json", "tauri", "tauri-plugin-deep-link", - "thiserror", + "thiserror 2.0.6", + "tracing", "windows-sys 0.59.0", - "zbus", + "zbus 4.4.0", ] [[package]] name = "tauri-runtime" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af12ad1af974b274ef1d32a94e6eba27a312b429ef28fcb98abc710df7f9151d" +checksum = "cce18d43f80d4aba3aa8a0c953bbe835f3d0f2370aca75e8dbb14bd4bab27958" dependencies = [ "dpi", "gtk", @@ -4181,16 +4427,16 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror", + "thiserror 2.0.6", "url", "windows", ] [[package]] name = "tauri-runtime-wry" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45e88aa0b11b302d836e6ea3e507a6359044c4a8bc86b865ba99868c695753d" +checksum = "9f442a38863e10129ffe2cec7bd09c2dcf8a098a3a27801a476a304d5bb991d2" dependencies = [ "gtk", "http", @@ -4214,9 +4460,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38b0230d6880cf6dd07b6d7dd7789a0869f98ac12146e0d18d1c1049215a045" +checksum = "9271a88f99b4adea0dc71d0baca4505475a0bbd139fb135f62958721aaa8fe54" dependencies = [ "brotli", "cargo_metadata", @@ -4224,6 +4470,7 @@ dependencies = [ "dunce", "glob", "html5ever", + "http", "infer", "json-patch", "kuchikiki", @@ -4240,8 +4487,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror", - "toml 0.8.2", + "thiserror 2.0.6", + "toml 0.8.19", "url", "urlpattern", "uuid", @@ -4260,9 +4507,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -4290,22 +4537,42 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -4320,12 +4587,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", - "itoa 1.0.11", + "itoa 1.0.14", "num-conv", "powerfmt", "serde", @@ -4341,9 +4608,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -4359,25 +4626,20 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -4399,7 +4661,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -4414,20 +4676,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -4450,21 +4711,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.22", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -4475,24 +4736,35 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -4503,9 +4775,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -4514,29 +4786,29 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] [[package]] name = "tray-icon" -version = "0.19.0" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533fc2d4105e0e3d96ce1c71f2d308c9fbbe2ef9c587cab63dd627ab5bde218f" +checksum = "d48a05076dd272615d03033bf04f480199f7d1b66a8ac64d75c625fc4a70c06b" dependencies = [ "core-graphics", "crossbeam-channel", @@ -4549,7 +4821,7 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror", + "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -4638,26 +4910,11 @@ dependencies = [ "unic-common", ] -[[package]] -name = "unicode-bidi" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" - [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -4688,9 +4945,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -4723,10 +4980,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "uuid" -version = "1.10.0" +name = "utf16_iter" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom 0.2.15", "rand 0.8.5", @@ -4736,13 +5005,13 @@ dependencies = [ [[package]] name = "uuid-macro-internal" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a" +checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -4816,9 +5085,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -4827,36 +5096,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4864,28 +5133,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -4910,9 +5179,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ "bitflags 2.6.0", "rustix", @@ -4922,9 +5191,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.4" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -4956,9 +5225,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -4966,9 +5235,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +checksum = "ea9fe1ebb156110ff855242c1101df158b822487e4957b0556d9ffce9db0f535" dependencies = [ "block2", "core-foundation 0.10.0", @@ -5048,7 +5317,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -5057,7 +5326,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ - "thiserror", + "thiserror 1.0.69", "windows", "windows-core 0.58.0", ] @@ -5147,7 +5416,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -5158,7 +5427,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", ] [[package]] @@ -5443,6 +5712,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.52.0" @@ -5454,15 +5732,26 @@ dependencies = [ ] [[package]] -name = "wry" -version = "0.44.1" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "440600584cfbd8b0d28eace95c1f2c253db05dae43780b79380aa1e868f04c73" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wry" +version = "0.47.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ce51277d65170f6379d8cda935c80e3c2d1f0ff712a123c8bddb11b31a4b73" dependencies = [ "base64 0.22.1", - "block", - "cocoa", - "core-graphics", + "block2", + "cookie", "crossbeam-channel", "dpi", "dunce", @@ -5475,15 +5764,19 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "objc", - "objc_id", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", "once_cell", "percent-encoding", "raw-window-handle", "sha2", "soup3", "tao-macros", - "thiserror", + "thiserror 1.0.69", + "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", @@ -5525,10 +5818,34 @@ dependencies = [ ] [[package]] -name = "zbus" -version = "4.0.1" +name = "yoke" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" dependencies = [ "async-broadcast", "async-executor", @@ -5540,7 +5857,6 @@ dependencies = [ "async-task", "async-trait", "blocking", - "derivative", "enumflags2", "event-listener", "futures-core", @@ -5554,28 +5870,71 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", - "tokio", "tracing", "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1162094dc63b1629fcc44150bcceeaa80798cd28bcbe7fa987b65a034c258608" +dependencies = [ + "async-broadcast", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener", + "futures-core", + "futures-util", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.6.20", + "xdg-home", + "zbus_macros 5.1.1", + "zbus_names 4.1.0", + "zvariant 5.1.0", ] [[package]] name = "zbus_macros" -version = "4.0.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", + "syn 2.0.90", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zbus_macros" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd2dcdce3e2727f7d74b7e33b5a89539b3cc31049562137faf7ae4eb86cd16d" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.90", + "zbus_names 4.1.0", + "zvariant 5.1.0", + "zvariant_utils 3.0.2", ] [[package]] @@ -5586,7 +5945,19 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_names" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.6.20", + "zvariant 5.1.0", ] [[package]] @@ -5607,7 +5978,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.90", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", ] [[package]] @@ -5617,39 +6009,103 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] -name = "zvariant" -version = "4.0.0" +name = "zerovec" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive 4.2.0", +] + +[[package]] +name = "zvariant" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", "url", - "zvariant_derive", + "winnow 0.6.20", + "zvariant_derive 5.1.0", + "zvariant_utils 3.0.2", ] [[package]] name = "zvariant_derive" -version = "4.0.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", - "zvariant_utils", + "syn 2.0.90", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zvariant_derive" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.90", + "zvariant_utils 3.0.2", ] [[package]] name = "zvariant_utils" -version = "1.1.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", +] + +[[package]] +name = "zvariant_utils" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.90", + "winnow 0.6.20", ] diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 7c6483cf..bed70858 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -68,7 +68,7 @@ features = ["vendored"] [dependencies.rustbreak] version = "2" -features = ["bin_enc"] # You can also use "yaml_enc" or "bin_enc" +features = [] # You can also use "yaml_enc" or "bin_enc" [dependencies.reqwest] version = "0.12" diff --git a/desktop/src-tauri/src/auth.rs b/desktop/src-tauri/src/auth.rs index 82e8da17..61446a07 100644 --- a/desktop/src-tauri/src/auth.rs +++ b/desktop/src-tauri/src/auth.rs @@ -90,9 +90,7 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc let path_chunks: Vec<&str> = path.split("/").collect(); if path_chunks.len() != 3 { app.emit("auth/failed", ()).unwrap(); - return Err(RemoteAccessError::GenericErrror( - "Invalid number of handshake chunks".to_string(), - )); + return Err(RemoteAccessError::InvalidResponse); } let base_url = { @@ -163,9 +161,7 @@ async fn auth_initiate_wrapper() -> Result<(), RemoteAccessError> { let response = client.post(endpoint.to_string()).json(&body).send().await?; if response.status() != 200 { - return Err("Failed to create redirect URL. Please try again later." - .to_string() - .into()); + return Err(RemoteAccessError::InvalidRedirect); } let redir_url = response.text().await?; @@ -187,6 +183,18 @@ pub async fn auth_initiate<'a>() -> Result<(), String> { Ok(()) } +#[tauri::command] +pub fn retry_connect(state: tauri::State<'_, Mutex>) -> Result<(), ()> { + let (app_status, user) = setup()?; + + let mut guard = state.lock().unwrap(); + guard.status = app_status; + guard.user = user; + drop(guard); + + Ok(()) +} + pub fn setup() -> Result<(AppStatus, Option), ()> { let data = DB.borrow_data().unwrap(); diff --git a/desktop/src-tauri/src/db.rs b/desktop/src-tauri/src/db.rs index ea7f922c..38b11fcf 100644 --- a/desktop/src-tauri/src/db.rs +++ b/desktop/src-tauri/src/db.rs @@ -6,8 +6,9 @@ use std::{ }; use directories::BaseDirs; -use rustbreak::{deser::Bincode, PathDatabase}; -use serde::{Deserialize, Serialize}; +use log::debug; +use rustbreak::{DeSerError, DeSerializer, PathDatabase}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use url::Url; use crate::DB; @@ -20,21 +21,36 @@ pub struct DatabaseAuth { pub client_id: String, } +// Strings are version names for a particular game #[derive(Serialize, Clone, Deserialize)] +#[serde(tag = "type")] pub enum DatabaseGameStatus { - Remote, - Downloading, - Installed, - Updating, + Remote {}, + Queued { version_name: String }, + Downloading { version_name: String }, + SetupRequired { version_name: String }, + Installed { version_name: String }, + Updating { version_name: String }, + Uninstalling {}, +} - Uninstalling, +#[derive(Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct GameVersion { + pub version_index: usize, + pub version_name: String, + pub launch_command: String, + pub setup_command: String, + pub platform: String, } #[derive(Serialize, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct DatabaseGames { pub install_dirs: Vec, + // Guaranteed to exist if the game also exists in the app state map pub games_statuses: HashMap, + pub game_versions: HashMap>, } #[derive(Serialize, Clone, Deserialize)] @@ -47,8 +63,22 @@ pub struct Database { pub static DATA_ROOT_DIR: LazyLock> = LazyLock::new(|| Mutex::new(BaseDirs::new().unwrap().data_dir().join("drop"))); +// Custom JSON serializer to support everything we need +#[derive(Debug, Default, Clone)] +pub struct DropDatabaseSerializer; + +impl DeSerializer for DropDatabaseSerializer { + fn serialize(&self, val: &T) -> rustbreak::error::DeSerResult> { + serde_json::to_vec(val).map_err(|e| DeSerError::Internal(e.to_string())) + } + + fn deserialize(&self, s: R) -> rustbreak::error::DeSerResult { + serde_json::from_reader(s).map_err(|e| DeSerError::Internal(e.to_string())) + } +} + pub type DatabaseInterface = - rustbreak::Database; + rustbreak::Database; pub trait DatabaseImpls { fn set_up_database() -> DatabaseInterface; @@ -61,26 +91,31 @@ impl DatabaseImpls for DatabaseInterface { let db_path = data_root_dir.join("drop.db"); let games_base_dir = data_root_dir.join("games"); - let default = Database { - auth: None, - base_url: "".to_string(), - games: DatabaseGames { - install_dirs: vec![games_base_dir.to_str().unwrap().to_string()], - games_statuses: HashMap::new(), - }, - }; + debug!("Creating data directory at {:?}", data_root_dir); + create_dir_all(data_root_dir.clone()).unwrap(); + debug!("Creating games directory"); + create_dir_all(games_base_dir.clone()).unwrap(); + #[allow(clippy::let_and_return)] - let db = match fs::exists(db_path.clone()).unwrap() { + let exists = fs::exists(db_path.clone()).unwrap(); + + match exists { true => PathDatabase::load_from_path(db_path).expect("Database loading failed"), false => { - create_dir_all(data_root_dir.clone()).unwrap(); - create_dir_all(games_base_dir.clone()).unwrap(); - - PathDatabase::create_at_path(db_path, default).unwrap() + let default = Database { + auth: None, + base_url: "".to_string(), + games: DatabaseGames { + install_dirs: vec![games_base_dir.to_str().unwrap().to_string()], + games_statuses: HashMap::new(), + game_versions: HashMap::new(), + }, + }; + debug!("Creating database at path {}", db_path.as_os_str().to_str().unwrap()); + PathDatabase::create_at_path(db_path, default) + .expect("Database could not be created") } - }; - - db + } } fn database_is_set_up(&self) -> bool { @@ -94,7 +129,7 @@ impl DatabaseImpls for DatabaseInterface { } #[tauri::command] -pub fn add_new_download_dir(new_dir: String) -> Result<(), String> { +pub fn add_download_dir(new_dir: String) -> Result<(), String> { // Check the new directory is all good let new_dir_path = Path::new(&new_dir); if new_dir_path.exists() { @@ -107,8 +142,8 @@ pub fn add_new_download_dir(new_dir: String) -> Result<(), String> { let dir_contents = new_dir_path .read_dir() .map_err(|e| format!("Unable to check directory contents: {}", e))?; - if dir_contents.count() == 0 { - return Err("Path is not empty".to_string()); + if dir_contents.count() != 0 { + return Err("Directory is not empty".to_string()); } } else { create_dir_all(new_dir_path) @@ -117,8 +152,33 @@ pub fn add_new_download_dir(new_dir: String) -> Result<(), String> { // Add it to the dictionary let mut lock = DB.borrow_data_mut().unwrap(); + if lock.games.install_dirs.contains(&new_dir) { + return Err("Download directory already used".to_string()); + } lock.games.install_dirs.push(new_dir); drop(lock); + DB.save().unwrap(); Ok(()) } + +#[tauri::command] +pub fn delete_download_dir(index: usize) -> Result<(), String> { + let mut lock = DB.borrow_data_mut().unwrap(); + lock.games.install_dirs.remove(index); + drop(lock); + DB.save().unwrap(); + + Ok(()) +} + +// Will, in future, return disk/remaining size +// Just returns the directories that have been set up +#[tauri::command] +pub fn fetch_download_dir_stats() -> Result, String> { + let lock = DB.borrow_data().unwrap(); + let directories = lock.games.install_dirs.clone(); + drop(lock); + + Ok(directories) +} diff --git a/desktop/src-tauri/src/downloads/download_agent.rs b/desktop/src-tauri/src/downloads/download_agent.rs index 9ae8ba81..52855cda 100644 --- a/desktop/src-tauri/src/downloads/download_agent.rs +++ b/desktop/src-tauri/src/downloads/download_agent.rs @@ -1,20 +1,24 @@ use crate::auth::generate_authorization_header; use crate::db::DatabaseImpls; use crate::downloads::manifest::{DropDownloadContext, DropManifest}; +use crate::downloads::progress_object::ProgressHandle; use crate::remote::RemoteAccessError; use crate::DB; -use log::info; +use log::{debug, error, info}; use rayon::ThreadPoolBuilder; use std::fmt::{Display, Formatter}; use std::fs::{create_dir_all, File}; +use std::io; use std::path::Path; -use std::sync::Mutex; +use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex}; use urlencoding::encode; #[cfg(target_os = "linux")] use rustix::fs::{fallocate, FallocateFlags}; use super::download_logic::download_game_chunk; +use super::download_manager::DownloadManagerSignal; use super::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}; use super::progress_object::ProgressObject; @@ -25,51 +29,66 @@ pub struct GameDownloadAgent { pub target_download_dir: usize, contexts: Mutex>, pub manifest: Mutex>, - pub progress: ProgressObject, + pub progress: Arc, + sender: Sender, } #[derive(Debug)] pub enum GameDownloadError { - CommunicationError(RemoteAccessError), - ChecksumError, - SetupError(String), - LockError, + Communication(RemoteAccessError), + Checksum, + Setup(SetupError), + Lock, + IoError(io::Error), + DownloadError, +} + +#[derive(Debug)] +pub enum SetupError { + Context, } impl Display for GameDownloadError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - GameDownloadError::CommunicationError(error) => write!(f, "{}", error), - GameDownloadError::SetupError(error) => write!(f, "{}", error), - GameDownloadError::LockError => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"), - GameDownloadError::ChecksumError => write!(f, "Checksum failed to validate for download"), + GameDownloadError::Communication(error) => write!(f, "{}", error), + GameDownloadError::Setup(error) => write!(f, "{:?}", error), + GameDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"), + GameDownloadError::Checksum => write!(f, "Checksum failed to validate for download"), + GameDownloadError::IoError(error) => write!(f, "{}", error), + GameDownloadError::DownloadError => write!(f, "Download failed. See Download Manager status for specific error"), } } } impl GameDownloadAgent { - pub fn new(id: String, version: String, target_download_dir: usize) -> Self { + pub fn new( + id: String, + version: String, + target_download_dir: usize, + sender: Sender, + ) -> Self { // Don't run by default - let status = DownloadThreadControl::new(DownloadThreadControlFlag::Stop); + let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop); Self { id, version, - control_flag: status.clone(), + control_flag, manifest: Mutex::new(None), target_download_dir, contexts: Mutex::new(Vec::new()), - progress: ProgressObject::new(0, 0), + progress: Arc::new(ProgressObject::new(0, 0, sender.clone())), + sender, } } // Blocking - // Requires mutable self - pub fn setup_download(&mut self) -> Result<(), GameDownloadError> { + pub fn setup_download(&self) -> Result<(), GameDownloadError> { self.ensure_manifest_exists()?; info!("Ensured manifest exists"); - self.generate_contexts()?; - info!("Generated contexts"); + self.ensure_contexts()?; + info!("Ensured contexts exists"); self.control_flag.set(DownloadThreadControlFlag::Go); @@ -77,23 +96,23 @@ impl GameDownloadAgent { } // Blocking - pub fn download(&mut self) -> Result<(), GameDownloadError> { + pub fn download(&self) -> Result<(), GameDownloadError> { self.setup_download()?; - self.run(); + self.set_progress_object_params(); + self.run().map_err(|_| GameDownloadError::DownloadError)?; Ok(()) } - pub fn ensure_manifest_exists(&mut self) -> Result<(), GameDownloadError> { + pub fn ensure_manifest_exists(&self) -> Result<(), GameDownloadError> { if self.manifest.lock().unwrap().is_some() { return Ok(()); } - // Explicitly propagate error self.download_manifest() } - fn download_manifest(&mut self) -> Result<(), GameDownloadError> { + fn download_manifest(&self) -> Result<(), GameDownloadError> { let base_url = DB.fetch_base_url(); let manifest_url = base_url .join( @@ -115,35 +134,52 @@ impl GameDownloadAgent { .unwrap(); if response.status() != 200 { - return Err(GameDownloadError::CommunicationError( - format!( - "Failed to download game manifest: {} {}", + return Err(GameDownloadError::Communication( + RemoteAccessError::ManifestDownloadFailed( response.status(), - response.text().unwrap() - ) - .into(), + response.text().unwrap(), + ), )); } let manifest_download = response.json::().unwrap(); - let length = manifest_download - .values() - .map(|chunk| { - return chunk.lengths.iter().sum::(); - }) - .sum::(); - let chunk_count = manifest_download - .values() - .map(|chunk| chunk.lengths.len()) - .sum(); - self.progress = ProgressObject::new(length, chunk_count); if let Ok(mut manifest) = self.manifest.lock() { *manifest = Some(manifest_download); return Ok(()); } - Err(GameDownloadError::LockError) + Err(GameDownloadError::Lock) + } + + fn set_progress_object_params(&self) { + // Avoid re-setting it + if self.progress.get_max() != 0 { + return; + } + + let lock = self.contexts.lock().unwrap(); + let length = lock.len(); + + let chunk_count = lock.iter().map(|chunk| chunk.length).sum(); + + debug!("Setting ProgressObject max to {}", chunk_count); + self.progress.set_max(chunk_count); + debug!("Setting ProgressObject size to {}", length); + self.progress.set_size(length); + debug!("Setting ProgressObject time to now"); + self.progress.set_time_now(); + } + + pub fn ensure_contexts(&self) -> Result<(), GameDownloadError> { + let context_lock = self.contexts.lock().unwrap(); + if !context_lock.is_empty() { + return Ok(()); + } + drop(context_lock); + + self.generate_contexts()?; + Ok(()) } pub fn generate_contexts(&self) -> Result<(), GameDownloadError> { @@ -152,7 +188,6 @@ impl GameDownloadAgent { drop(db_lock); let manifest = self.manifest.lock().unwrap().clone().unwrap(); - let version = self.version.clone(); let game_id = self.id.clone(); let data_base_dir_path = Path::new(&data_base_dir); @@ -173,19 +208,20 @@ impl GameDownloadAgent { for (index, length) in chunk.lengths.iter().enumerate() { contexts.push(DropDownloadContext { file_name: raw_path.to_string(), - version: version.to_string(), + version: chunk.version_name.to_string(), offset: running_offset, index, game_id: game_id.to_string(), path: path.clone(), checksum: chunk.checksums[index].clone(), + length: *length, }); running_offset += *length as u64; } #[cfg(target_os = "linux")] if running_offset > 0 { - fallocate(file, FallocateFlags::empty(), 0, running_offset).unwrap(); + let _ = fallocate(file, FallocateFlags::empty(), 0, running_offset); } } @@ -194,35 +230,69 @@ impl GameDownloadAgent { return Ok(()); } - Err(GameDownloadError::SetupError( - String::from("Failed to generate download contexts"), - )) + Err(GameDownloadError::Setup(SetupError::Context)) } - pub fn run(&self) { - const DOWNLOAD_MAX_THREADS: usize = 4; + pub fn run(&self) -> Result<(), ()> { + info!("downloading game: {}", self.id); + const DOWNLOAD_MAX_THREADS: usize = 1; let pool = ThreadPoolBuilder::new() .num_threads(DOWNLOAD_MAX_THREADS) .build() .unwrap(); + let completed_indexes = Arc::new(Mutex::new(Vec::new())); + let completed_indexes_loop_arc = completed_indexes.clone(); + pool.scope(move |scope| { let contexts = self.contexts.lock().unwrap(); for (index, context) in contexts.iter().enumerate() { let context = context.clone(); let control_flag = self.control_flag.clone(); // Clone arcs - let progress = self.progress.get(index); + let progress = self.progress.get(index); // Clone arcs + let progress_handle = ProgressHandle::new(progress, self.progress.clone()); + let completed_indexes_ref = completed_indexes_loop_arc.clone(); scope.spawn(move |_| { - info!( - "starting download for file {} {}", - context.file_name, context.index - ); - download_game_chunk(context, control_flag, progress).unwrap(); + match download_game_chunk(context.clone(), control_flag, progress_handle) { + Ok(res) => { + if res { + let mut lock = completed_indexes_ref.lock().unwrap(); + lock.push(index); + } + } + Err(e) => { + error!("GameDownloadError: {}", e); + self.sender.send(DownloadManagerSignal::Error(e)).unwrap(); + } + } }); } - }) + }); + + let mut context_lock = self.contexts.lock().unwrap(); + let mut completed_lock = completed_indexes.lock().unwrap(); + + // Sort desc so we don't have to modify indexes + completed_lock.sort_by(|a, b| b.cmp(a)); + + for index in completed_lock.iter() { + context_lock.remove(*index); + } + + // If we're not out of contexts, we're not done, so we don't fire completed + if !context_lock.is_empty() { + info!("da for {} exited without completing", self.id.clone()); + return Ok(()); + } + + // We've completed + self.sender + .send(DownloadManagerSignal::Completed(self.id.clone())) + .unwrap(); + + Ok(()) } } diff --git a/desktop/src-tauri/src/downloads/download_commands.rs b/desktop/src-tauri/src/downloads/download_commands.rs index 54b93152..23b8bab8 100644 --- a/desktop/src-tauri/src/downloads/download_commands.rs +++ b/desktop/src-tauri/src/downloads/download_commands.rs @@ -1,53 +1,57 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Mutex; -use log::info; -use rayon::spawn; - -use crate::{downloads::download_agent::GameDownloadAgent, AppState}; +use crate::AppState; #[tauri::command] pub fn download_game( game_id: String, game_version: String, + install_dir: usize, state: tauri::State<'_, Mutex>, ) -> Result<(), String> { - info!("beginning game download..."); - - let mut download_agent = GameDownloadAgent::new(game_id.clone(), game_version.clone(), 0); - // Setup download requires mutable - download_agent.setup_download().unwrap(); - - let mut lock: std::sync::MutexGuard<'_, AppState> = state.lock().unwrap(); - let download_agent_ref = Arc::new(download_agent); - lock.game_downloads - .insert(game_id, download_agent_ref.clone()); - - // Run it in another thread - spawn(move || { - // Run doesn't require mutable - download_agent_ref.clone().run(); - }); - - Ok(()) + state + .lock() + .unwrap() + .download_manager + .queue_game(game_id, game_version, install_dir) + .map_err(|_| "An error occurred while communicating with the download manager.".to_string()) } #[tauri::command] -pub fn get_game_download_progress( - state: tauri::State<'_, Mutex>, - game_id: String, -) -> Result { - let download_agent = use_download_agent(state, game_id)?; - - let progress = &download_agent.progress; - - Ok(progress.get_progress()) +pub fn pause_game_downloads(state: tauri::State<'_, Mutex>) { + state.lock().unwrap().download_manager.pause_downloads() } +#[tauri::command] +pub fn resume_game_downloads(state: tauri::State<'_, Mutex>) { + state.lock().unwrap().download_manager.resume_downloads() +} + +#[tauri::command] +pub fn move_game_in_queue( + state: tauri::State<'_, Mutex>, + old_index: usize, + new_index: usize, +) { + state + .lock() + .unwrap() + .download_manager + .rearrange(old_index, new_index) +} + +/* +#[tauri::command] +pub fn get_current_write_speed(state: tauri::State<'_, Mutex>) {} +*/ + +/* fn use_download_agent( state: tauri::State<'_, Mutex>, game_id: String, ) -> Result, String> { let lock = state.lock().unwrap(); - let download_agent = lock.game_downloads.get(&game_id).ok_or("Invalid game ID")?; + let download_agent = lock.download_manager.get(&game_id).ok_or("Invalid game ID")?; Ok(download_agent.clone()) // Clones the Arc, not the underlying data structure } +*/ diff --git a/desktop/src-tauri/src/downloads/download_logic.rs b/desktop/src-tauri/src/downloads/download_logic.rs index 2a0c21f2..6735c56b 100644 --- a/desktop/src-tauri/src/downloads/download_logic.rs +++ b/desktop/src-tauri/src/downloads/download_logic.rs @@ -3,21 +3,21 @@ use crate::db::DatabaseImpls; use crate::downloads::manifest::DropDownloadContext; use crate::remote::RemoteAccessError; use crate::DB; +use log::warn; use md5::{Context, Digest}; use reqwest::blocking::Response; use std::io::Read; -use std::sync::atomic::AtomicUsize; use std::{ fs::{File, OpenOptions}, - io::{self, BufWriter, ErrorKind, Seek, SeekFrom, Write}, + io::{self, BufWriter, Seek, SeekFrom, Write}, path::PathBuf, - sync::Arc, }; use urlencoding::encode; use super::download_agent::GameDownloadError; use super::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}; +use super::progress_object::ProgressHandle; pub struct DropWriter { hasher: Context, @@ -39,17 +39,19 @@ impl DropWriter { // 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::new( ErrorKind::Other, format!("Unable to write to hasher: {}", e), ) })?; + */ self.destination.write(buf) } fn flush(&mut self) -> io::Result<()> { - self.hasher.flush()?; + // self.hasher.flush()?; self.destination.flush() } } @@ -64,7 +66,7 @@ pub struct DropDownloadPipeline { pub source: R, pub destination: DropWriter, pub control_flag: DownloadThreadControl, - pub progress: Arc, + pub progress: ProgressHandle, pub size: usize, } impl DropDownloadPipeline { @@ -72,7 +74,7 @@ impl DropDownloadPipeline { source: Response, destination: DropWriter, control_flag: DownloadThreadControl, - progress: Arc, + progress: ProgressHandle, size: usize, ) -> Self { Self { @@ -99,8 +101,7 @@ impl DropDownloadPipeline { current_size += bytes_read; buf_writer.write_all(©_buf[0..bytes_read])?; - self.progress - .fetch_add(bytes_read, std::sync::atomic::Ordering::Relaxed); + self.progress.add(bytes_read); if current_size == self.size { break; @@ -119,10 +120,11 @@ impl DropDownloadPipeline { pub fn download_game_chunk( ctx: DropDownloadContext, control_flag: DownloadThreadControl, - progress: Arc, + progress: ProgressHandle, ) -> Result { // If we're paused if control_flag.get() == DownloadThreadControlFlag::Stop { + progress.set(0); return Ok(false); } @@ -146,7 +148,14 @@ pub fn download_game_chunk( .get(chunk_url) .header("Authorization", header) .send() - .map_err(|e| GameDownloadError::CommunicationError(RemoteAccessError::FetchError(e)))?; + .map_err(|e| GameDownloadError::Communication(e.into()))?; + + if response.status() != 200 { + warn!("{}", response.text().unwrap()); + return Err(GameDownloadError::Communication( + RemoteAccessError::InvalidCodeError(400), + )); + } let mut destination = DropWriter::new(ctx.path); @@ -158,10 +167,8 @@ pub fn download_game_chunk( let content_length = response.content_length(); if content_length.is_none() { - return Err(GameDownloadError::CommunicationError( - RemoteAccessError::GenericErrror( - "Invalid download endpoint, missing Content-Length header.".to_owned(), - ), + return Err(GameDownloadError::Communication( + RemoteAccessError::InvalidResponse, )); } @@ -173,17 +180,21 @@ pub fn download_game_chunk( content_length.unwrap().try_into().unwrap(), ); - let completed = pipeline.copy().unwrap(); + let completed = pipeline.copy().map_err(GameDownloadError::IoError)?; if !completed { return Ok(false); }; - let checksum = pipeline.finish().unwrap(); + /* + let checksum = pipeline + .finish() + .map_err(|e| GameDownloadError::IoError(e))?; let res = hex::encode(checksum.0); if res != ctx.checksum { - return Err(GameDownloadError::ChecksumError); + return Err(GameDownloadError::Checksum); } + */ Ok(true) } diff --git a/desktop/src-tauri/src/downloads/download_manager.rs b/desktop/src-tauri/src/downloads/download_manager.rs new file mode 100644 index 00000000..f04135fa --- /dev/null +++ b/desktop/src-tauri/src/downloads/download_manager.rs @@ -0,0 +1,191 @@ +use std::{ + any::Any, + collections::VecDeque, + fmt::Debug, + sync::{ + mpsc::{SendError, Sender}, + Arc, Mutex, MutexGuard, + }, + thread::JoinHandle, +}; + +use log::info; +use serde::Serialize; + +use super::{ + download_agent::{GameDownloadAgent, GameDownloadError}, + download_manager_builder::CurrentProgressObject, + progress_object::ProgressObject, + queue::Queue, +}; + +pub enum DownloadManagerSignal { + /// Resumes (or starts) the DownloadManager + Go, + /// Pauses the DownloadManager + Stop, + /// Called when a GameDownloadAgent has fully completed a download. + Completed(String), + /// Generates and appends a GameDownloadAgent + /// to the registry and queue + Queue(String, String, usize), + /// Tells the Manager to stop the current + /// download and return + Finish, + Cancel, + /// Any error which occurs in the agent + Error(GameDownloadError), + /// Pushes UI update + Update, +} +pub enum DownloadManagerStatus { + Downloading, + Paused, + Empty, + Error(GameDownloadError), +} + +#[derive(Serialize, Clone)] +pub enum GameDownloadStatus { + Queued, + Downloading, + Paused, + Error, +} + +/// Accessible front-end for the DownloadManager +/// +/// The system works entirely through signals, both internally and externally, +/// all of which are accessible through the DownloadManagerSignal type, but +/// should not be used directly. Rather, signals are abstracted through this +/// interface. +/// +/// The actual download queue may be accessed through the .edit() function, +/// which provides raw access to the underlying queue. +/// THIS EDITING IS BLOCKING!!! +pub struct DownloadManager { + terminator: JoinHandle>, + download_queue: Queue, + progress: CurrentProgressObject, + command_sender: Sender, +} +pub struct GameDownloadAgentQueueStandin { + pub id: String, + pub status: Mutex, + pub progress: Arc, +} +impl From> for GameDownloadAgentQueueStandin { + fn from(value: Arc) -> Self { + Self { + id: value.id.clone(), + status: Mutex::from(GameDownloadStatus::Queued), + progress: value.progress.clone(), + } + } +} +impl Debug for GameDownloadAgentQueueStandin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GameDownloadAgentQueueStandin") + .field("id", &self.id) + .finish() + } +} + +#[allow(dead_code)] +impl DownloadManager { + pub fn new( + terminator: JoinHandle>, + download_queue: Queue, + progress: CurrentProgressObject, + command_sender: Sender, + ) -> Self { + Self { + terminator, + download_queue, + progress, + command_sender, + } + } + + pub fn queue_game( + &self, + id: String, + version: String, + target_download_dir: usize, + ) -> Result<(), SendError> { + info!("Adding game id {}", id); + self.command_sender.send(DownloadManagerSignal::Queue( + id, + version, + target_download_dir, + ))?; + self.command_sender.send(DownloadManagerSignal::Go) + } + pub fn edit(&self) -> MutexGuard<'_, VecDeque>> { + self.download_queue.edit() + } + pub fn read_queue(&self) -> VecDeque> { + self.download_queue.read() + } + pub fn get_current_game_download_progress(&self) -> Option { + let progress_object = (*self.progress.lock().unwrap()).clone()?; + Some(progress_object.get_progress()) + } + pub fn rearrange_string(&self, id: String, new_index: usize) { + let mut queue = self.edit(); + let current_index = get_index_from_id(&mut queue, id).unwrap(); + let to_move = queue.remove(current_index).unwrap(); + queue.insert(new_index, to_move); + self.command_sender + .send(DownloadManagerSignal::Update) + .unwrap(); + } + pub fn rearrange(&self, current_index: usize, new_index: usize) { + let needs_pause = current_index == 0 || new_index == 0; + if needs_pause { + self.command_sender + .send(DownloadManagerSignal::Cancel) + .unwrap(); + } + + info!("moving {} to {}", current_index, new_index); + + let mut queue = self.edit(); + let to_move = queue.remove(current_index).unwrap(); + queue.insert(new_index, to_move); + + info!("new queue: {:?}", queue); + + if needs_pause { + self.command_sender.send(DownloadManagerSignal::Go).unwrap(); + } + self.command_sender + .send(DownloadManagerSignal::Update) + .unwrap(); + } + pub fn pause_downloads(&self) { + self.command_sender + .send(DownloadManagerSignal::Stop) + .unwrap(); + } + pub fn resume_downloads(&self) { + self.command_sender.send(DownloadManagerSignal::Go).unwrap(); + } + pub fn ensure_terminated(self) -> Result, Box> { + self.command_sender + .send(DownloadManagerSignal::Finish) + .unwrap(); + self.terminator.join() + } +} + +/// Takes in the locked value from .edit() and attempts to +/// get the index of whatever game_id is passed in +fn get_index_from_id( + queue: &mut MutexGuard<'_, VecDeque>>, + id: String, +) -> Option { + queue + .iter() + .position(|download_agent| download_agent.id == id) +} diff --git a/desktop/src-tauri/src/downloads/download_manager_builder.rs b/desktop/src-tauri/src/downloads/download_manager_builder.rs new file mode 100644 index 00000000..1b338533 --- /dev/null +++ b/desktop/src-tauri/src/downloads/download_manager_builder.rs @@ -0,0 +1,347 @@ +use std::{ + collections::HashMap, + sync::{ + mpsc::{channel, Receiver, Sender}, + Arc, Mutex, + }, + thread::{spawn, JoinHandle}, +}; + +use log::{error, info}; +use tauri::{AppHandle, Emitter}; + +use crate::{ + db::DatabaseGameStatus, + library::{on_game_complete, GameUpdateEvent, QueueUpdateEvent, QueueUpdateEventQueueData}, + DB, +}; + +use super::{ + download_agent::{GameDownloadAgent, GameDownloadError}, + download_manager::{ + DownloadManager, DownloadManagerSignal, DownloadManagerStatus, + GameDownloadAgentQueueStandin, GameDownloadStatus, + }, + download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}, + progress_object::ProgressObject, + queue::Queue, +}; + +/* + +Welcome to the download manager, the most overengineered, glorious piece of bullshit. + +The download manager takes a queue of game_ids and their associated +GameDownloadAgents, and then, one-by-one, executes them. It provides an interface +to interact with the currently downloading agent, and manage the queue. + +When the DownloadManager is initialised, it is designed to provide a reference +which can be used to provide some instructions (the DownloadManagerInterface), +but other than that, it runs without any sort of interruptions. + +It does this by opening up two data structures. Primarily is the command_receiver, +and mpsc (multi-channel-single-producer) which allows commands to be sent from +the Interface, and queued up for the Manager to process. + +These have been mapped in the DownloadManagerSignal docs. + +The other way to interact with the DownloadManager is via the donwload_queue, +which is just a collection of ids which may be rearranged to suit +whichever download queue order is required. + ++----------------------------------------------------------------------------+ +| DO NOT ATTEMPT TO ADD OR REMOVE FROM THE QUEUE WITHOUT USING SIGNALS!! | +| THIS WILL CAUSE A DESYNC BETWEEN THE DOWNLOAD AGENT REGISTRY AND THE QUEUE | +| WHICH HAS NOT BEEN ACCOUNTED FOR | ++----------------------------------------------------------------------------+ + +This download queue does not actually own any of the GameDownloadAgents. It is +simply a id-based reference system. The actual Agents are stored in the +download_agent_registry HashMap, as ordering is no issue here. This is why +appending or removing from the download_queue must be done via signals. + +Behold, my madness - quexeky + +*/ + +// Refactored to consolidate this type. It's a monster. +pub type CurrentProgressObject = Arc>>>; + +pub struct DownloadManagerBuilder { + download_agent_registry: HashMap>, + download_queue: Queue, + command_receiver: Receiver, + sender: Sender, + progress: CurrentProgressObject, + status: Arc>, + app_handle: AppHandle, + + current_download_agent: Option>, // Should be the only game download agent in the map with the "Go" flag + current_download_thread: Mutex>>, + active_control_flag: Option, +} + +impl DownloadManagerBuilder { + pub fn build(app_handle: AppHandle) -> DownloadManager { + let queue = Queue::new(); + let (command_sender, command_receiver) = channel(); + let active_progress = Arc::new(Mutex::new(None)); + let status = Arc::new(Mutex::new(DownloadManagerStatus::Empty)); + + let manager = Self { + download_agent_registry: HashMap::new(), + download_queue: queue.clone(), + command_receiver, + status: status.clone(), + sender: command_sender.clone(), + progress: active_progress.clone(), + app_handle, + + current_download_agent: None, + current_download_thread: Mutex::new(None), + active_control_flag: None, + }; + + let terminator = spawn(|| manager.manage_queue()); + + DownloadManager::new(terminator, queue, active_progress, command_sender) + } + + fn set_game_status(&self, id: String, status: DatabaseGameStatus) { + let mut db_handle = DB.borrow_data_mut().unwrap(); + db_handle + .games + .games_statuses + .insert(id.clone(), status.clone()); + drop(db_handle); + DB.save().unwrap(); + self.app_handle + .emit( + &format!("update_game/{}", id), + GameUpdateEvent { + game_id: id, + status, + }, + ) + .unwrap(); + } + + fn push_manager_update(&self) { + let queue = self.download_queue.read(); + let queue_objs: Vec = queue + .iter() + .map(|interface| QueueUpdateEventQueueData { + id: interface.id.clone(), + status: interface.status.lock().unwrap().clone(), + progress: interface.progress.get_progress(), + }) + .collect(); + + let event_data = QueueUpdateEvent { queue: queue_objs }; + self.app_handle.emit("update_queue", event_data).unwrap(); + } + + fn remove_and_cleanup_game(&mut self, game_id: &String) -> Arc { + self.download_queue.pop_front(); + let download_agent = self.download_agent_registry.remove(game_id).unwrap(); + self.cleanup_current_download(); + download_agent + } + + // CAREFUL WITH THIS FUNCTION + // Make sure the download thread is terminated + fn cleanup_current_download(&mut self) { + self.active_control_flag = None; + *self.progress.lock().unwrap() = None; + self.current_download_agent = None; + + let mut download_thread_lock = self.current_download_thread.lock().unwrap(); + *download_thread_lock = None; + drop(download_thread_lock); + } + + fn manage_queue(mut self) -> Result<(), ()> { + loop { + let signal = match self.command_receiver.recv() { + Ok(signal) => signal, + Err(_) => return Err(()), + }; + + match signal { + DownloadManagerSignal::Go => { + self.manage_go_signal(); + } + DownloadManagerSignal::Stop => { + self.manage_stop_signal(); + } + DownloadManagerSignal::Completed(game_id) => { + self.manage_completed_signal(game_id); + } + DownloadManagerSignal::Queue(game_id, version, target_download_dir) => { + self.manage_queue_signal(game_id, version, target_download_dir); + } + DownloadManagerSignal::Finish => { + if let Some(active_control_flag) = self.active_control_flag { + active_control_flag.set(DownloadThreadControlFlag::Stop) + } + return Ok(()); + } + DownloadManagerSignal::Error(e) => { + self.manage_error_signal(e); + } + DownloadManagerSignal::Cancel => { + self.manage_cancel_signal(); + } + DownloadManagerSignal::Update => { + self.push_manager_update(); + } + }; + } + } + + fn manage_stop_signal(&mut self) { + info!("Got signal 'Stop'"); + if let Some(active_control_flag) = self.active_control_flag.clone() { + active_control_flag.set(DownloadThreadControlFlag::Stop); + } + } + + fn manage_completed_signal(&mut self, game_id: String) { + info!("Got signal 'Completed'"); + if let Some(interface) = &self.current_download_agent { + // When if let chains are stabilised, combine these two statements + if interface.id == game_id { + info!("Popping consumed data"); + let download_agent = self.remove_and_cleanup_game(&game_id); + + if let Err(error) = + on_game_complete(game_id, download_agent.version.clone(), &self.app_handle) + { + self.sender + .send(DownloadManagerSignal::Error( + GameDownloadError::Communication(error), + )) + .unwrap(); + } + } + } + self.sender.send(DownloadManagerSignal::Update).unwrap(); + self.sender.send(DownloadManagerSignal::Go).unwrap(); + } + + fn manage_queue_signal(&mut self, id: String, version: String, target_download_dir: usize) { + info!("Got signal Queue"); + let download_agent = Arc::new(GameDownloadAgent::new( + id.clone(), + version, + target_download_dir, + self.sender.clone(), + )); + let agent_status = GameDownloadStatus::Queued; + let interface_data = GameDownloadAgentQueueStandin { + id: id.clone(), + status: Mutex::new(agent_status), + progress: download_agent.progress.clone(), + }; + let version_name = download_agent.version.clone(); + self.download_agent_registry + .insert(interface_data.id.clone(), download_agent); + self.download_queue.append(interface_data); + + self.set_game_status(id, DatabaseGameStatus::Queued { version_name }); + self.sender.send(DownloadManagerSignal::Update).unwrap(); + } + + fn manage_go_signal(&mut self) { + if !(!self.download_agent_registry.is_empty() && !self.download_queue.empty()) { + return; + } + + if self.current_download_agent.is_some() { + info!("skipping go signal due to existing download job"); + return; + } + + info!("current download queue: {:?}", self.download_queue.read()); + let agent_data = self.download_queue.read().front().unwrap().clone(); + info!("starting download for {}", agent_data.id.clone()); + let download_agent = self + .download_agent_registry + .get(&agent_data.id) + .unwrap() + .clone(); + self.current_download_agent = Some(agent_data); + // Cloning option should be okay because it only clones the Arc inside, not the AgentInterfaceData + let agent_data = self.current_download_agent.clone().unwrap(); + + let version_name = download_agent.version.clone(); + + let progress_object = download_agent.progress.clone(); + *self.progress.lock().unwrap() = Some(progress_object); + + let active_control_flag = download_agent.control_flag.clone(); + self.active_control_flag = Some(active_control_flag.clone()); + + let sender = self.sender.clone(); + + info!("Spawning download"); + let mut download_thread_lock = self.current_download_thread.lock().unwrap(); + *download_thread_lock = Some(spawn(move || { + match download_agent.download() { + // Returns once we've exited the download + // (not necessarily completed) + // The download agent will fire the completed event for us + Ok(_) => {} + // If an error occurred while *starting* the download + Err(err) => { + error!("error while managing download: {}", err); + sender.send(DownloadManagerSignal::Error(err)).unwrap(); + } + }; + })); + + // Set status for game + let mut status_handle = agent_data.status.lock().unwrap(); + *status_handle = GameDownloadStatus::Downloading; + + // Set flags for download manager + active_control_flag.set(DownloadThreadControlFlag::Go); + self.set_status(DownloadManagerStatus::Downloading); + self.set_game_status( + self.current_download_agent.as_ref().unwrap().id.clone(), + DatabaseGameStatus::Downloading { version_name }, + ); + } + fn manage_error_signal(&mut self, error: GameDownloadError) { + let current_status = self.current_download_agent.clone().unwrap(); + + self.remove_and_cleanup_game(¤t_status.id); // Remove all the locks and shit + + let mut lock = current_status.status.lock().unwrap(); + *lock = GameDownloadStatus::Error; + self.set_status(DownloadManagerStatus::Error(error)); + + let game_id = self.current_download_agent.as_ref().unwrap().id.clone(); + self.set_game_status(game_id, DatabaseGameStatus::Remote {}); + + self.sender.send(DownloadManagerSignal::Update).unwrap(); + } + fn manage_cancel_signal(&mut self) { + if let Some(current_flag) = &self.active_control_flag { + current_flag.set(DownloadThreadControlFlag::Stop); + } + + let mut download_thread_lock = self.current_download_thread.lock().unwrap(); + if let Some(current_download_thread) = download_thread_lock.take() { + current_download_thread.join().unwrap(); + } + drop(download_thread_lock); + + info!("cancel waited for download to finish"); + + self.cleanup_current_download(); + } + fn set_status(&self, status: DownloadManagerStatus) { + *self.status.lock().unwrap() = status; + } +} diff --git a/desktop/src-tauri/src/downloads/manifest.rs b/desktop/src-tauri/src/downloads/manifest.rs index 2bb15520..d8158614 100644 --- a/desktop/src-tauri/src/downloads/manifest.rs +++ b/desktop/src-tauri/src/downloads/manifest.rs @@ -4,11 +4,13 @@ use std::path::PathBuf; pub type DropManifest = HashMap; #[derive(Serialize, Deserialize, Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct DropChunk { pub permissions: usize, pub ids: Vec, pub checksums: Vec, pub lengths: Vec, + pub version_name: String, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -20,4 +22,5 @@ pub struct DropDownloadContext { pub game_id: String, pub path: PathBuf, pub checksum: String, + pub length: usize, } diff --git a/desktop/src-tauri/src/downloads/mod.rs b/desktop/src-tauri/src/downloads/mod.rs index 9559d8a2..0102c33e 100644 --- a/desktop/src-tauri/src/downloads/mod.rs +++ b/desktop/src-tauri/src/downloads/mod.rs @@ -1,6 +1,9 @@ pub mod download_agent; pub mod download_commands; mod download_logic; +pub mod download_manager; +pub mod download_manager_builder; mod download_thread_control_flag; mod manifest; -mod progress_object; \ No newline at end of file +mod progress_object; +pub mod queue; diff --git a/desktop/src-tauri/src/downloads/progress_object.rs b/desktop/src-tauri/src/downloads/progress_object.rs index f3be4fbd..143656dd 100644 --- a/desktop/src-tauri/src/downloads/progress_object.rs +++ b/desktop/src-tauri/src/downloads/progress_object.rs @@ -1,33 +1,111 @@ -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, +use std::{ + sync::{ + atomic::{AtomicUsize, Ordering}, + mpsc::Sender, + Arc, Mutex, + }, + time::Instant, }; +use log::info; + +use super::download_manager::DownloadManagerSignal; + #[derive(Clone)] pub struct ProgressObject { - max: usize, - progress_instances: Arc>>, + max: Arc>, + progress_instances: Arc>>>, + start: Arc>, + sender: Sender, + + points_towards_update: Arc, + points_to_push_update: Arc>, } -impl ProgressObject { - pub fn new(max: usize, length: usize) -> Self { - let arr = (0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect(); +pub struct ProgressHandle { + progress: Arc, + progress_object: Arc, +} + +impl ProgressHandle { + pub fn new(progress: Arc, progress_object: Arc) -> Self { Self { - max, - progress_instances: Arc::new(arr), + progress, + progress_object, } } + pub fn set(&self, amount: usize) { + self.progress.store(amount, Ordering::Relaxed); + } + pub fn add(&self, amount: usize) { + self.progress + .fetch_add(amount, std::sync::atomic::Ordering::Relaxed); + self.progress_object.check_push_update(amount); + } +} + +static PROGRESS_UPDATES: usize = 100; + +impl ProgressObject { + pub fn new(max: usize, length: usize, sender: Sender) -> Self { + let arr = Mutex::new((0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect()); + // TODO: consolidate this calculation with the set_max function below + let points_to_push_update = max / PROGRESS_UPDATES; + Self { + max: Arc::new(Mutex::new(max)), + progress_instances: Arc::new(arr), + start: Arc::new(Mutex::new(Instant::now())), + sender, + + points_towards_update: Arc::new(AtomicUsize::new(0)), + points_to_push_update: Arc::new(Mutex::new(points_to_push_update)), + } + } + + pub fn check_push_update(&self, amount_added: usize) { + let current_amount = self + .points_towards_update + .fetch_add(amount_added, Ordering::Relaxed); + + let to_update_handle = self.points_to_push_update.lock().unwrap(); + let to_update = *to_update_handle; + drop(to_update_handle); + + if current_amount < to_update { + return; + } + self.points_towards_update + .fetch_sub(to_update, Ordering::Relaxed); + self.sender.send(DownloadManagerSignal::Update).unwrap(); + } + + pub fn set_time_now(&self) { + *self.start.lock().unwrap() = Instant::now(); + } pub fn sum(&self) -> usize { self.progress_instances + .lock() + .unwrap() .iter() .map(|instance| instance.load(Ordering::Relaxed)) .sum() } - + pub fn get_max(&self) -> usize { + *self.max.lock().unwrap() + } + pub fn set_max(&self, new_max: usize) { + *self.max.lock().unwrap() = new_max; + *self.points_to_push_update.lock().unwrap() = new_max / PROGRESS_UPDATES; + info!("points to push update: {}", new_max / PROGRESS_UPDATES); + } + pub fn set_size(&self, length: usize) { + *self.progress_instances.lock().unwrap() = + (0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect(); + } pub fn get_progress(&self) -> f64 { - self.sum() as f64 / self.max as f64 + self.sum() as f64 / self.get_max() as f64 } pub fn get(&self, index: usize) -> Arc { - self.progress_instances[index].clone() + self.progress_instances.lock().unwrap()[index].clone() } } diff --git a/desktop/src-tauri/src/downloads/queue.rs b/desktop/src-tauri/src/downloads/queue.rs new file mode 100644 index 00000000..0ea65cae --- /dev/null +++ b/desktop/src-tauri/src/downloads/queue.rs @@ -0,0 +1,73 @@ +use std::{ + collections::VecDeque, + sync::{Arc, Mutex, MutexGuard}, +}; + +use super::download_manager::GameDownloadAgentQueueStandin; + +#[derive(Clone)] +pub struct Queue { + inner: Arc>>>, +} + +#[allow(dead_code)] +impl Queue { + pub fn new() -> Self { + Self { + inner: Arc::new(Mutex::new(VecDeque::new())), + } + } + pub fn read(&self) -> VecDeque> { + self.inner.lock().unwrap().clone() + } + pub fn edit(&self) -> MutexGuard<'_, VecDeque>> { + self.inner.lock().unwrap() + } + pub fn pop_front(&self) -> Option> { + self.edit().pop_front() + } + pub fn empty(&self) -> bool { + self.inner.lock().unwrap().len() == 0 + } + /// Either inserts `interface` at the specified index, or appends to + /// the back of the deque if index is greater than the length of the deque + pub fn insert(&self, interface: GameDownloadAgentQueueStandin, index: usize) { + if self.read().len() > index { + self.append(interface); + } else { + self.edit().insert(index, Arc::new(interface)); + } + } + pub fn append(&self, interface: GameDownloadAgentQueueStandin) { + self.edit().push_back(Arc::new(interface)); + } + pub fn pop_front_if_equal( + &self, + game_id: String, + ) -> Option> { + let mut queue = self.edit(); + let front = match queue.front() { + Some(front) => front, + None => return None, + }; + if front.id == game_id { + return queue.pop_front(); + } + None + } + pub fn get_by_id(&self, game_id: String) -> Option { + self.read().iter().position(|data| data.id == game_id) + } + pub fn move_to_index_by_id(&self, game_id: String, new_index: usize) -> Result<(), ()> { + let index = match self.get_by_id(game_id) { + Some(index) => index, + None => return Err(()), + }; + let existing = match self.edit().remove(index) { + Some(existing) => existing, + None => return Err(()), + }; + self.edit().insert(new_index, existing); + Ok(()) + } +} diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 0acc2290..f9b5ab26 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -2,21 +2,25 @@ mod auth; mod db; mod downloads; mod library; -mod p2p; +// mod p2p; mod remote; mod settings; #[cfg(test)] mod tests; use crate::db::DatabaseImpls; -use crate::downloads::download_agent::GameDownloadAgent; -use auth::{auth_initiate, generate_authorization_header, recieve_handshake}; -use db::{add_new_download_dir, DatabaseInterface, DATA_ROOT_DIR}; +use auth::{auth_initiate, generate_authorization_header, recieve_handshake, retry_connect}; +use db::{ + add_download_dir, delete_download_dir, fetch_download_dir_stats, DatabaseInterface, + DATA_ROOT_DIR, +}; use downloads::download_commands::*; +use downloads::download_manager::DownloadManager; +use downloads::download_manager_builder::DownloadManagerBuilder; use http::{header::*, response::Builder as ResponseBuilder}; -use library::{fetch_game, fetch_library, Game}; -use log::{info, LevelFilter}; -use log4rs::append::console::{ConsoleAppender, Target}; +use library::{fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, Game}; +use log::{debug, info, LevelFilter}; +use log4rs::append::console::ConsoleAppender; use log4rs::append::file::FileAppender; use log4rs::config::{Appender, Root}; use log4rs::encode::pattern::PatternEncoder; @@ -28,6 +32,7 @@ use std::{ collections::HashMap, sync::{LazyLock, Mutex}, }; +use tauri::{AppHandle, Manager}; use tauri_plugin_deep_link::DeepLinkExt; #[derive(Clone, Copy, Serialize)] @@ -39,6 +44,7 @@ pub enum AppStatus { SignedInNeedsReauth, ServerUnavailable, } + #[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct User { @@ -57,7 +63,7 @@ pub struct AppState { games: HashMap, #[serde(skip_serializing)] - game_downloads: HashMap>, + download_manager: Arc, } #[tauri::command] @@ -68,14 +74,14 @@ fn fetch_state(state: tauri::State<'_, Mutex>) -> Result AppState { +fn setup(handle: AppHandle) -> AppState { let logfile = FileAppender::builder() .encoder(Box::new(PatternEncoder::new("{t}|{l}|{f} - {m}{n}"))) .build(DATA_ROOT_DIR.lock().unwrap().join("./drop.log")) .unwrap(); let console = ConsoleAppender::builder() - .encoder(Box::new(PatternEncoder::new("{l} - {m}\n"))) + .encoder(Box::new(PatternEncoder::new("{t}|{l}|{f} - {m}{n}\n"))) .build(); let config = Config::builder() @@ -92,24 +98,28 @@ fn setup() -> AppState { log4rs::init_config(config).unwrap(); - //env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + let games = HashMap::new(); + let download_manager = Arc::new(DownloadManagerBuilder::build(handle)); + debug!("Checking if database is set up"); let is_set_up = DB.database_is_set_up(); if !is_set_up { return AppState { status: AppStatus::NotConfigured, user: None, - games: HashMap::new(), - game_downloads: HashMap::new(), + games, + download_manager, }; } + debug!("Database is set up"); + let (app_status, user) = auth::setup().unwrap(); AppState { status: app_status, user, - games: HashMap::new(), - game_downloads: HashMap::new(), + games, + download_manager, } } @@ -117,9 +127,6 @@ pub static DB: LazyLock = LazyLock::new(DatabaseInterface::se #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { - let state = setup(); - info!("initialized drop client"); - let mut builder = tauri::Builder::default().plugin(tauri_plugin_dialog::init()); #[cfg(desktop)] @@ -132,25 +139,37 @@ pub fn run() { builder .plugin(tauri_plugin_deep_link::init()) - .manage(Mutex::new(state)) .invoke_handler(tauri::generate_handler![ // DB fetch_state, // Auth auth_initiate, + retry_connect, // Remote use_remote, gen_drop_url, // Library fetch_library, fetch_game, - add_new_download_dir, + add_download_dir, + delete_download_dir, + fetch_download_dir_stats, + fetch_game_status, + fetch_game_verion_options, // Downloads download_game, - get_game_download_progress, + move_game_in_queue, + pause_game_downloads, + resume_game_downloads, ]) .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_dialog::init()) .setup(|app| { + let handle = app.handle().clone(); + let state = setup(handle); + info!("initialized drop client"); + app.manage(Mutex::new(state)); + #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] { use tauri_plugin_deep_link::DeepLinkExt; diff --git a/desktop/src-tauri/src/library.rs b/desktop/src-tauri/src/library.rs index 65f9ec10..0e25c526 100644 --- a/desktop/src-tauri/src/library.rs +++ b/desktop/src-tauri/src/library.rs @@ -1,16 +1,19 @@ use std::sync::Mutex; use serde::{Deserialize, Serialize}; -use serde_json::json; +use tauri::Emitter; use tauri::{AppHandle, Manager}; +use urlencoding::encode; use crate::db::DatabaseGameStatus; use crate::db::DatabaseImpls; +use crate::db::GameVersion; +use crate::downloads::download_manager::GameDownloadStatus; use crate::remote::RemoteAccessError; use crate::{auth::generate_authorization_header, AppState, DB}; #[derive(serde::Serialize)] -struct FetchGameStruct { +pub struct FetchGameStruct { game: Game, status: DatabaseGameStatus, } @@ -29,8 +32,38 @@ pub struct Game { m_cover_id: String, m_image_library: Vec, } +#[derive(serde::Serialize, Clone)] +pub struct GameUpdateEvent { + pub game_id: String, + pub status: DatabaseGameStatus, +} -fn fetch_library_logic(app: AppHandle) -> Result { +#[derive(Serialize, Clone)] +pub struct QueueUpdateEventQueueData { + pub id: String, + pub status: GameDownloadStatus, + pub progress: f64, +} + +#[derive(serde::Serialize, Clone)] +pub struct QueueUpdateEvent { + pub queue: Vec, +} + +// Game version with some fields missing and size information +#[derive(serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct GameVersionOption { + version_index: usize, + version_name: String, + platform: String, + setup_command: String, + launch_command: String, + delta: bool, + // total_size: usize, +} + +fn fetch_library_logic(app: AppHandle) -> Result, RemoteAccessError> { let base_url = DB.fetch_base_url(); let library_url = base_url.join("/api/v1/client/user/library")?; @@ -46,7 +79,7 @@ fn fetch_library_logic(app: AppHandle) -> Result { return Err(response.status().as_u16().into()); } - let games = response.json::>()?; + let games: Vec = response.json::>()?; let state = app.state::>(); let mut handle = state.lock().unwrap(); @@ -59,31 +92,28 @@ fn fetch_library_logic(app: AppHandle) -> Result { db_handle .games .games_statuses - .insert(game.id.clone(), DatabaseGameStatus::Remote); + .insert(game.id.clone(), DatabaseGameStatus::Remote {}); } } drop(handle); - Ok(json!(games.clone()).to_string()) + Ok(games) } #[tauri::command] -pub fn fetch_library(app: AppHandle) -> Result { - let result = fetch_library_logic(app); - - if result.is_err() { - return Err(result.err().unwrap().to_string()); - } - - Ok(result.unwrap()) +pub fn fetch_library(app: AppHandle) -> Result, String> { + fetch_library_logic(app).map_err(|e| e.to_string()) } -fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result { +fn fetch_game_logic( + id: String, + app: tauri::AppHandle, +) -> Result { let state = app.state::>(); - let handle = state.lock().unwrap(); + let mut state_handle = state.lock().unwrap(); - let game = handle.games.get(&id); + let game = state_handle.games.get(&id); if let Some(game) = game { let db_handle = DB.borrow_data().unwrap(); @@ -97,15 +127,55 @@ fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result()?; + state_handle.games.insert(id.clone(), game.clone()); + + let mut db_handle = DB.borrow_data_mut().unwrap(); + + db_handle + .games + .games_statuses + .entry(id) + .or_insert(DatabaseGameStatus::Remote {}); + + let data = FetchGameStruct { + game: game.clone(), + status: db_handle + .games + .games_statuses + .get(&game.id) + .unwrap() + .clone(), + }; + + Ok(data) } #[tauri::command] -pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result { +pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result { let result = fetch_game_logic(id, app); if result.is_err() { @@ -114,3 +184,107 @@ pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result { Ok(result.unwrap()) } + +#[tauri::command] +pub fn fetch_game_status(id: String) -> Result { + let db_handle = DB.borrow_data().unwrap(); + let status = db_handle + .games + .games_statuses + .get(&id) + .unwrap_or(&DatabaseGameStatus::Remote {}) + .clone(); + drop(db_handle); + + Ok(status) +} + +fn fetch_game_verion_options_logic( + game_id: String, +) -> Result, RemoteAccessError> { + let base_url = DB.fetch_base_url(); + + let endpoint = + base_url.join(format!("/api/v1/client/metadata/versions?id={}", game_id).as_str())?; + let header = generate_authorization_header(); + + let client = reqwest::blocking::Client::new(); + let response = client + .get(endpoint.to_string()) + .header("Authorization", header) + .send()?; + + if response.status() != 200 { + return Err(RemoteAccessError::InvalidCodeError( + response.status().into(), + )); + } + + let data = response.json::>()?; + + Ok(data) +} + +#[tauri::command] +pub fn fetch_game_verion_options(game_id: String) -> Result, String> { + fetch_game_verion_options_logic(game_id).map_err(|e| e.to_string()) +} + +pub fn on_game_complete( + game_id: String, + version_name: String, + app_handle: &AppHandle, +) -> Result<(), RemoteAccessError> { + // Fetch game version information from remote + let base_url = DB.fetch_base_url(); + + let endpoint = base_url.join( + format!( + "/api/v1/client/metadata/version?id={}&version={}", + game_id, + encode(&version_name) + ) + .as_str(), + )?; + let header = generate_authorization_header(); + + let client = reqwest::blocking::Client::new(); + let response = client + .get(endpoint.to_string()) + .header("Authorization", header) + .send()?; + + let data = response.json::()?; + + let mut handle = DB.borrow_data_mut().unwrap(); + handle + .games + .game_versions + .entry(game_id.clone()) + .or_default() + .insert(version_name.clone(), data.clone()); + drop(handle); + DB.save().unwrap(); + + let status = if data.setup_command.is_empty() { + DatabaseGameStatus::Installed { version_name } + } else { + DatabaseGameStatus::SetupRequired { version_name } + }; + + let mut db_handle = DB.borrow_data_mut().unwrap(); + db_handle + .games + .games_statuses + .insert(game_id.clone(), status.clone()); + drop(db_handle); + DB.save().unwrap(); + app_handle + .emit( + &format!("update_game/{}", game_id), + GameUpdateEvent { game_id, status }, + ) + .unwrap(); + + Ok(()) +} diff --git a/desktop/src-tauri/src/p2p/registration.rs b/desktop/src-tauri/src/p2p/registration.rs new file mode 100644 index 00000000..0926515e --- /dev/null +++ b/desktop/src-tauri/src/p2p/registration.rs @@ -0,0 +1,17 @@ +use crate::{auth::generate_authorization_header, db::DatabaseImpls, remote::RemoteAccessError, DB}; + + +pub async fn register() -> Result { + let base_url = DB.fetch_base_url(); + let registration_url = base_url.join("/api/v1/client/capability").unwrap(); + let header = generate_authorization_header(); + + + let client = reqwest::blocking::Client::new(); + client + .post(registration_url) + .header("Authorization", header) + .send()?; + + return Ok(String::new()) +} \ No newline at end of file diff --git a/desktop/src-tauri/src/remote.rs b/desktop/src-tauri/src/remote.rs index e415c529..0a53c2b9 100644 --- a/desktop/src-tauri/src/remote.rs +++ b/desktop/src-tauri/src/remote.rs @@ -1,43 +1,53 @@ use std::{ fmt::{Display, Formatter}, - sync::Mutex, + sync::{Arc, Mutex}, }; +use http::StatusCode; use log::{info, warn}; use serde::Deserialize; use url::{ParseError, Url}; use crate::{AppState, AppStatus, DB}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum RemoteAccessError { - FetchError(reqwest::Error), + FetchError(Arc), ParsingError(ParseError), InvalidCodeError(u16), - GenericErrror(String), + InvalidEndpoint, + HandshakeFailed, + GameNotFound, + InvalidResponse, + InvalidRedirect, + ManifestDownloadFailed(StatusCode, String), } impl Display for RemoteAccessError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { RemoteAccessError::FetchError(error) => write!(f, "{}", error), - RemoteAccessError::GenericErrror(error) => write!(f, "{}", error), RemoteAccessError::ParsingError(parse_error) => { write!(f, "{}", parse_error) } RemoteAccessError::InvalidCodeError(error) => write!(f, "HTTP {}", error), + RemoteAccessError::InvalidEndpoint => write!(f, "Invalid drop endpoint"), + RemoteAccessError::HandshakeFailed => write!(f, "Failed to complete handshake"), + RemoteAccessError::GameNotFound => write!(f, "Could not find game on server"), + RemoteAccessError::InvalidResponse => write!(f, "Server returned an invalid response"), + RemoteAccessError::InvalidRedirect => write!(f, "Server redirect was invalid"), + RemoteAccessError::ManifestDownloadFailed(status, response) => write!( + f, + "Failed to download game manifest: {} {}", + status, response + ), } } } impl From for RemoteAccessError { fn from(err: reqwest::Error) -> Self { - RemoteAccessError::FetchError(err) - } -} -impl From for RemoteAccessError { - fn from(err: String) -> Self { - RemoteAccessError::GenericErrror(err) + RemoteAccessError::FetchError(Arc::new(err)) } } impl From for RemoteAccessError { @@ -74,7 +84,7 @@ async fn use_remote_logic<'a>( if result.app_name != "Drop" { warn!("user entered drop endpoint that connected, but wasn't identified as Drop"); - return Err("Not a valid Drop endpoint".to_string().into()); + return Err(RemoteAccessError::InvalidEndpoint); } let mut app_state = state.lock().unwrap(); diff --git a/desktop/types.ts b/desktop/types.ts index b63d9043..5702594c 100644 --- a/desktop/types.ts +++ b/desktop/types.ts @@ -1,4 +1,3 @@ -import type { User } from "@prisma/client"; import type { Component } from "vue"; export type NavigationItem = { @@ -12,11 +11,31 @@ export type QuickActionNav = { notifications?: number; action: () => Promise; }; + +export type User = { + id: string; + username: string; + admin: boolean; + displayName: string; + profilePicture: string; +}; + export type AppState = { status: AppStatus; user?: User; }; +export type Game = { + id: string; + mName: string; + mShortDescription: string; + mDescription: string; + mIconId: string; + mBannerId: string; + mCoverId: string; + mImageLibrary: string[]; +}; + export enum AppStatus { NotConfigured = "NotConfigured", SignedOut = "SignedOut", @@ -25,10 +44,17 @@ export enum AppStatus { ServerUnavailable = "ServerUnavailable", } -export enum GameStatus { +export enum GameStatusEnum { Remote = "Remote", + Queued = "Queued", Downloading = "Downloading", Installed = "Installed", Updating = "Updating", Uninstalling = "Uninstalling", + SetupRequired = "SetupRequired", } + +export type GameStatus = { + type: GameStatusEnum; + version_name?: string; +}; diff --git a/desktop/yarn.lock b/desktop/yarn.lock index da122f89..a4fa8b8d 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -1075,47 +1075,6 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== -"@prisma/client@5.20.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.20.0.tgz#4fc9f2b2341c9c997c139df4445688dd6b39663b" - integrity sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA== - -"@prisma/debug@5.20.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.20.0.tgz#c6d1cf6e3c6e9dba150347f13ca200b1d66cc9fc" - integrity sha512-oCx79MJ4HSujokA8S1g0xgZUGybD4SyIOydoHMngFYiwEwYDQ5tBQkK5XoEHuwOYDKUOKRn/J0MEymckc4IgsQ== - -"@prisma/engines-version@5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284": - version "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284.tgz#9a53b13cdcfd706ae54198111000f33c63655c39" - integrity sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA== - -"@prisma/engines@5.20.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.20.0.tgz#86fe407e55219d33d03ebc26dc829a422faed545" - integrity sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ== - dependencies: - "@prisma/debug" "5.20.0" - "@prisma/engines-version" "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284" - "@prisma/fetch-engine" "5.20.0" - "@prisma/get-platform" "5.20.0" - -"@prisma/fetch-engine@5.20.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.20.0.tgz#b917880fb08f654981f14ca49923031b39683586" - integrity sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw== - dependencies: - "@prisma/debug" "5.20.0" - "@prisma/engines-version" "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284" - "@prisma/get-platform" "5.20.0" - -"@prisma/get-platform@5.20.0": - version "5.20.0" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.20.0.tgz#c1a53a8d8af67f2b4a6b97dd4d25b1c603236804" - integrity sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA== - dependencies: - "@prisma/debug" "5.20.0" - "@rollup/plugin-alias@^5.1.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz#53601d88cda8b1577aa130b4a6e452283605bf26" @@ -1376,10 +1335,10 @@ dependencies: "@tauri-apps/api" "^2.0.0" -"@tauri-apps/plugin-dialog@~2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.0.tgz#f1e2840c7f824572a76b375fd1b538a36f28de14" - integrity sha512-ApNkejXP2jpPBSifznPPcHTXxu9/YaRW+eJ+8+nYwqp0lLUtebFHG4QhxitM43wwReHE81WAV1DQ/b+2VBftOA== +"@tauri-apps/plugin-dialog@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.1.tgz#cca38f2ef361c6d92495f5aa12154492cf3fa779" + integrity sha512-fnUrNr6EfvTqdls/ufusU7h6UbNFzLKvHk/zTuOiBq01R3dTODqwctZlzakdbfSp/7pNwTKvgKTAgl/NAP/Z0Q== dependencies: "@tauri-apps/api" "^2.0.0" @@ -2816,7 +2775,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@2.3.3, fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4345,15 +4304,6 @@ pretty-bytes@^6.1.1: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz#38cd6bb46f47afbf667c202cfc754bffd2016a3b" integrity sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ== -prisma@^5.20.0: - version "5.20.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.20.0.tgz#f2ab266a0d59383506886e7acbff0dbf322f4c7e" - integrity sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig== - dependencies: - "@prisma/engines" "5.20.0" - optionalDependencies: - fsevents "2.3.3" - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -4847,6 +4797,11 @@ smob@^1.0.0: resolved "https://registry.yarnpkg.com/smob/-/smob-1.5.0.tgz#85d79a1403abf128d24d3ebc1cdc5e1a9548d3ab" integrity sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig== +sortablejs@1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8" + integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w== + source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" @@ -5553,6 +5508,13 @@ vue@^3.5.5, vue@latest: "@vue/server-renderer" "3.5.11" "@vue/shared" "3.5.11" +vuedraggable@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270" + integrity sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww== + dependencies: + sortablejs "1.14.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"