From 537d27d10bd8338a60d1c579ac80588621c513de Mon Sep 17 00:00:00 2001 From: Tunglies Date: Mon, 18 Aug 2025 02:02:25 +0800 Subject: [PATCH] fix: clippy errors with new config (#4428) * refactor: improve code quality with clippy fixes and standardized logging - Replace dangerous unwrap()/expect() calls with proper error handling - Standardize logging from log:: to logging\! macro with Type:: classifications - Fix app handle panics with graceful fallback patterns - Improve error resilience across 35+ modules without breaking functionality - Reduce clippy warnings from 300+ to 0 in main library code * chore: update Cargo.toml configuration * refactor: resolve all clippy warnings - Fix Arc clone warnings using explicit Arc::clone syntax across 9 files - Add #[allow(clippy::expect_used)] to test functions for appropriate expect usage - Remove no-effect statements from debug code cleanup - Apply clippy auto-fixes for dbg\! macro removals and path statements - Achieve zero clippy warnings on all targets with -D warnings flag * chore: update Cargo.toml clippy configuration * refactor: simplify macOS job configuration and improve caching * refactor: remove unnecessary async/await from service and proxy functions * refactor: streamline pnpm installation in CI configuration * refactor: simplify error handling and remove unnecessary else statements * refactor: replace async/await with synchronous locks for core management * refactor: add workflow_dispatch trigger to clippy job * refactor: convert async functions to synchronous for service management * refactor: convert async functions to synchronous for UWP tool invocation * fix: change wrong logging * refactor: convert proxy restoration functions to async * Revert "refactor: convert proxy restoration functions to async" This reverts commit b82f5d250b2af7151e4dfd7dd411630b34ed2c18. * refactor: update proxy restoration functions to return Result types * fix: handle errors during proxy restoration and update async function signatures * fix: handle errors during proxy restoration and update async function signatures * refactor: update restore_pac_proxy and restore_sys_proxy functions to async * fix: convert restore_pac_proxy and restore_sys_proxy functions to async * fix: await restore_sys_proxy calls in proxy restoration logic * fix: suppress clippy warnings for unused async functions in proxy restoration * fix: suppress clippy warnings for unused async functions in proxy restoration --- .github/workflows/clippy.yml | 1 + .github/workflows/dev.yml | 30 +- src-tauri/Cargo.lock | 347 +---------------- src-tauri/Cargo.toml | 57 ++- src-tauri/src/cmd/app.rs | 3 +- src-tauri/src/cmd/clash.rs | 63 +++- src-tauri/src/cmd/media_unlock_checker.rs | 429 ++++++++++++++++++---- src-tauri/src/cmd/proxy.rs | 12 +- src-tauri/src/cmd/save_profile.rs | 21 +- src-tauri/src/cmd/service.rs | 22 +- src-tauri/src/cmd/system.rs | 14 +- src-tauri/src/cmd/uwp.rs | 8 +- src-tauri/src/config/clash.rs | 12 +- src-tauri/src/config/config.rs | 28 +- src-tauri/src/config/draft.rs | 11 +- src-tauri/src/config/prfitem.rs | 31 +- src-tauri/src/config/profiles.rs | 24 +- src-tauri/src/core/backup.rs | 14 +- src-tauri/src/core/core.rs | 78 ++-- src-tauri/src/core/event_driven_proxy.rs | 107 +++--- src-tauri/src/core/handle.rs | 198 +++++----- src-tauri/src/core/hotkey.rs | 24 +- src-tauri/src/core/service.rs | 61 ++- src-tauri/src/core/sysopt.rs | 50 ++- src-tauri/src/core/tray/mod.rs | 99 +++-- src-tauri/src/core/win_uwp.rs | 2 +- src-tauri/src/enhance/merge.rs | 5 +- src-tauri/src/enhance/mod.rs | 4 +- src-tauri/src/enhance/script.rs | 65 +++- src-tauri/src/enhance/seq.rs | 42 ++- src-tauri/src/enhance/tun.rs | 12 +- src-tauri/src/feat/backup.rs | 4 +- src-tauri/src/feat/clash.rs | 9 +- src-tauri/src/feat/config.rs | 10 +- src-tauri/src/feat/profile.rs | 26 +- src-tauri/src/feat/proxy.rs | 11 +- src-tauri/src/feat/window.rs | 9 +- src-tauri/src/ipc/general.rs | 11 +- src-tauri/src/ipc/logs.rs | 15 +- src-tauri/src/ipc/monitor.rs | 4 +- src-tauri/src/lib.rs | 26 +- src-tauri/src/module/lightweight.rs | 5 +- src-tauri/src/module/sysinfo.rs | 16 +- src-tauri/src/state/proxy.rs | 10 +- src-tauri/src/utils/dirs.rs | 9 +- src-tauri/src/utils/init.rs | 9 +- src-tauri/src/utils/network.rs | 84 ++++- src-tauri/src/utils/resolve.rs | 63 +++- src-tauri/src/utils/server.rs | 3 +- 49 files changed, 1275 insertions(+), 923 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 669b0258..2dd00550 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -2,6 +2,7 @@ name: Clippy Lint on: pull_request: + workflow_dispatch: jobs: clippy: diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 573e80ba..519eb7d6 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -13,11 +13,6 @@ on: required: false type: boolean default: true - run_macos_x86_64: - description: "运行 macOS x86_64" - required: false - type: boolean - default: true permissions: write-all env: @@ -45,14 +40,13 @@ jobs: bundle: dmg id: macos-aarch64 input: run_macos_aarch64 - - os: macos-latest - target: x86_64-apple-darwin - bundle: dmg - id: macos-x86_64 - input: run_macos_x86_64 runs-on: ${{ matrix.os }} steps: + - name: Skip job if not selected + if: github.event.inputs[matrix.input] != 'true' + run: echo "Job ${{ matrix.id }} skipped as requested" + - name: Checkout Repository if: github.event.inputs[matrix.input] == 'true' uses: actions/checkout@v4 @@ -71,21 +65,22 @@ jobs: with: workspaces: src-tauri save-if: false - cache-all-crates: false + cache-all-crates: true shared-key: autobuild-shared - - name: Install Node - if: github.event.inputs[matrix.input] == 'true' - uses: actions/setup-node@v4 - with: - node-version: "20" - - uses: pnpm/action-setup@v4 name: Install pnpm if: github.event.inputs[matrix.input] == 'true' with: run_install: false + - name: Install Node + if: github.event.inputs[matrix.input] == 'true' + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + - name: Pnpm install and check if: github.event.inputs[matrix.input] == 'true' run: | @@ -93,6 +88,7 @@ jobs: pnpm run prebuild ${{ matrix.target }} - name: Release ${{ env.TAG_CHANNEL }} Version + if: github.event.inputs[matrix.input] == 'true' run: pnpm release-version ${{ env.TAG_NAME }} - name: Tauri build diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 466b2844..ce2c4104 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -74,15 +74,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -173,17 +164,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "arrayvec" version = "0.7.6" @@ -490,29 +470,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "av1-grain" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom 7.1.3", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" -dependencies = [ - "arrayvec", -] - [[package]] name = "axum" version = "0.6.20" @@ -591,12 +548,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" @@ -612,12 +563,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitstream-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" - [[package]] name = "block" version = "0.1.6" @@ -833,12 +778,6 @@ dependencies = [ "alloc-stdlib", ] -[[package]] -name = "built" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" - [[package]] name = "bumpalo" version = "3.19.0" @@ -1111,7 +1050,6 @@ version = "2.4.0" dependencies = [ "aes-gcm", "anyhow", - "async-trait", "base64 0.22.1", "boa_engine", "chrono", @@ -1122,14 +1060,11 @@ dependencies = [ "dirs 6.0.0", "dunce", "futures", - "futures-util", "gethostname 1.0.2", "getrandom 0.3.3", "hex", "hmac", - "image", "kode-bridge", - "lazy_static", "libc", "log", "log4rs", @@ -1166,7 +1101,6 @@ dependencies = [ "tauri-plugin-shell", "tauri-plugin-updater", "tauri-plugin-window-state", - "tempfile", "tokio", "users", "warp", @@ -1213,12 +1147,6 @@ dependencies = [ "objc", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colored" version = "2.2.0" @@ -1991,26 +1919,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -2081,21 +1989,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "exr" -version = "1.73.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - [[package]] name = "fast-float2" version = "0.2.3" @@ -2552,16 +2445,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gif" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.31.1" @@ -3378,37 +3261,11 @@ checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", "num-traits", "png", - "qoi", - "ravif", - "rayon", - "rgb", "tiff", - "zune-core", - "zune-jpeg", ] -[[package]] -name = "image-webp" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imgref" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" - [[package]] name = "indexmap" version = "1.9.3" @@ -3474,17 +3331,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "interprocess" version = "2.2.3" @@ -3747,12 +3593,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - [[package]] name = "libappindicator" version = "0.9.0" @@ -3783,16 +3623,6 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" -[[package]] -name = "libfuzzer-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" -dependencies = [ - "arbitrary", - "cc", -] - [[package]] name = "libloading" version = "0.7.4" @@ -3954,15 +3784,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - [[package]] name = "lru" version = "0.12.5" @@ -4051,16 +3872,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - [[package]] name = "md-5" version = "0.10.6" @@ -4323,12 +4134,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "notify-rust" version = "4.11.7" @@ -4390,17 +4195,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -4410,17 +4204,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -5403,25 +5186,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn 2.0.104", -] - [[package]] name = "prost" version = "0.12.6" @@ -5470,15 +5234,6 @@ dependencies = [ "psl-types", ] -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - [[package]] name = "quick-error" version = "2.0.1" @@ -5683,56 +5438,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rav1e" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" -dependencies = [ - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.12.1", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive 0.4.2", - "num-traits", - "once_cell", - "paste", - "profiling", - "rand 0.8.5", - "rand_chacha 0.3.1", - "simd_helpers", - "system-deps", - "thiserror 1.0.69", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.11.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -5964,12 +5669,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" - [[package]] name = "ring" version = "0.17.14" @@ -6621,15 +6320,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -7542,7 +7232,7 @@ dependencies = [ "libc", "log", "memmem", - "num-derive 0.3.3", + "num-derive", "num-traits", "ordered-float", "regex", @@ -8354,17 +8044,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -9812,30 +9491,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" -dependencies = [ - "zune-core", -] - [[package]] name = "zvariant" version = "5.6.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c59baf01..2736418b 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -30,7 +30,6 @@ boa_engine = "0.20.0" serde_json = "1.0.142" serde_yaml = "0.9.34" once_cell = "1.21.3" -lazy_static = "1.5.0" port_scanner = "0.1.5" delay_timer = "0.11.6" parking_lot = "0.12.4" @@ -41,12 +40,10 @@ tokio = { version = "1.47.1", features = [ "time", "sync", ] } -futures-util = "0.3.31" serde = { version = "1.0.219", features = ["derive"] } reqwest = { version = "0.12.23", features = ["json", "rustls-tls", "cookies"] } regex = "1.11.1" sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs" } -image = "0.25.6" tauri = { version = "2.7.0", features = [ "protocol-asset", "devtools", @@ -70,7 +67,6 @@ base64 = "0.22.1" getrandom = "0.3.3" futures = "0.3.31" sys-locale = "0.3.2" -async-trait = "0.1.89" libc = "0.2.175" gethostname = "1.0.2" hmac = "0.12.1" @@ -141,4 +137,55 @@ crate-type = ["staticlib", "cdylib", "rlib"] [dev-dependencies] criterion = "0.7.0" -tempfile = "3.20.0" + +[lints.clippy] +# Core categories - most important for code safety and correctness +correctness = { level = "deny", priority = -1 } +suspicious = { level = "deny", priority = -1 } + +# Critical safety lints - warn for now due to extensive existing usage +unwrap_used = "warn" +expect_used = "warn" +panic = "deny" +unimplemented = "deny" + +# Development quality lints +todo = "warn" +dbg_macro = "warn" +#print_stdout = "warn" +#print_stderr = "warn" + +# Performance lints for proxy application +clone_on_ref_ptr = "warn" +rc_clone_in_vec_init = "warn" +large_stack_arrays = "warn" +large_const_arrays = "warn" + +# Security lints +#integer_division = "warn" +#lossy_float_literal = "warn" +#default_numeric_fallback = "warn" + +# Mutex and async lints - strict control +async_yields_async = "deny" # Prevents missing await in async blocks +mutex_atomic = "deny" # Use atomics instead of Mutex +mutex_integer = "deny" # Use AtomicInt instead of Mutex +rc_mutex = "deny" # Single-threaded Rc with Mutex is wrong +unused_async = "deny" # Too many false positives in Tauri/framework code +await_holding_lock = "deny" +large_futures = "deny" +future_not_send = "deny" + +# Common style improvements +redundant_else = "deny" # Too many in existing code +needless_continue = "deny" # Too many in existing code +needless_raw_string_hashes = "deny" # Too many in existing code + +# Disable noisy categories for existing codebase but keep them available +#style = { level = "allow", priority = -1 } +#complexity = { level = "allow", priority = -1 } +#perf = { level = "allow", priority = -1 } +#pedantic = { level = "allow", priority = -1 } +#nursery = { level = "allow", priority = -1 } +#restriction = { level = "allow", priority = -1 } + diff --git a/src-tauri/src/cmd/app.rs b/src-tauri/src/cmd/app.rs index f862dbe7..cb8fd6a2 100644 --- a/src-tauri/src/cmd/app.rs +++ b/src-tauri/src/cmd/app.rs @@ -121,9 +121,8 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult Err(_) => { if icon_path.exists() { return Ok(icon_path.to_string_lossy().to_string()); - } else { - return Err("Failed to create temporary file".into()); } + return Err("Failed to create temporary file".into()); } }; diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs index 35cd29db..32a8ace9 100644 --- a/src-tauri/src/cmd/clash.rs +++ b/src-tauri/src/cmd/clash.rs @@ -44,7 +44,7 @@ pub async fn patch_clash_mode(payload: String) -> CmdResult { /// 切换Clash核心 #[tauri::command] pub async fn change_clash_core(clash_core: String) -> CmdResult> { - log::info!(target: "app", "changing core to {clash_core}"); + logging!(info, Type::Config, "changing core to {clash_core}"); match CoreManager::global() .change_core(Some(clash_core.clone())) @@ -54,14 +54,18 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult> // 切换内核后重启内核 match CoreManager::global().restart_core().await { Ok(_) => { - log::info!(target: "app", "core changed and restarted to {clash_core}"); + logging!( + info, + Type::Core, + "core changed and restarted to {clash_core}" + ); handle::Handle::notice_message("config_core::change_success", &clash_core); handle::Handle::refresh_clash(); Ok(None) } Err(err) => { let error_msg = format!("Core changed but failed to restart: {err}"); - log::error!(target: "app", "{error_msg}"); + logging!(error, Type::Core, "{error_msg}"); handle::Handle::notice_message("config_core::change_error", &error_msg); Ok(Some(error_msg)) } @@ -69,7 +73,7 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult> } Err(err) => { let error_msg = err.to_string(); - log::error!(target: "app", "failed to change core: {error_msg}"); + logging!(error, Type::Core, "failed to change core: {error_msg}"); handle::Handle::notice_message("config_core::change_error", &error_msg); Ok(Some(error_msg)) } @@ -141,7 +145,7 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult { // 保存DNS配置到文件 let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?; fs::write(&dns_path, yaml_str).map_err(|e| e.to_string())?; - log::info!(target: "app", "DNS config saved to {dns_path:?}"); + logging!(info, Type::Config, "DNS config saved to {dns_path:?}"); Ok(()) } @@ -162,20 +166,20 @@ pub fn apply_dns_config(apply: bool) -> CmdResult { let dns_path = match dirs::app_home_dir() { Ok(path) => path.join("dns_config.yaml"), Err(e) => { - log::error!(target: "app", "Failed to get home dir: {e}"); + logging!(error, Type::Config, "Failed to get home dir: {e}"); return; } }; if !dns_path.exists() { - log::warn!(target: "app", "DNS config file not found"); + logging!(warn, Type::Config, "DNS config file not found"); return; } let dns_yaml = match std::fs::read_to_string(&dns_path) { Ok(content) => content, Err(e) => { - log::error!(target: "app", "Failed to read DNS config: {e}"); + logging!(error, Type::Config, "Failed to read DNS config: {e}"); return; } }; @@ -188,12 +192,12 @@ pub fn apply_dns_config(apply: bool) -> CmdResult { patch } Err(e) => { - log::error!(target: "app", "Failed to parse DNS config: {e}"); + logging!(error, Type::Config, "Failed to parse DNS config: {e}"); return; } }; - log::info!(target: "app", "Applying DNS config from file"); + logging!(info, Type::Config, "Applying DNS config from file"); // 重新生成配置,确保DNS配置被正确应用 // 这里不调用patch_clash以避免将DNS配置写入config.yaml @@ -202,37 +206,53 @@ pub fn apply_dns_config(apply: bool) -> CmdResult { .patch_config(patch_config.clone()); // 首先重新生成配置 - if let Err(err) = Config::generate().await { - log::error!(target: "app", "Failed to regenerate config with DNS: {err}"); + if let Err(err) = Config::generate() { + logging!( + error, + Type::Config, + "Failed to regenerate config with DNS: {err}" + ); return; } // 然后应用新配置 if let Err(err) = CoreManager::global().update_config().await { - log::error!(target: "app", "Failed to apply config with DNS: {err}"); + logging!( + error, + Type::Config, + "Failed to apply config with DNS: {err}" + ); } else { - log::info!(target: "app", "DNS config successfully applied"); + logging!(info, Type::Config, "DNS config successfully applied"); handle::Handle::refresh_clash(); } } else { // 当关闭DNS设置时,不需要对配置进行任何修改 // 直接重新生成配置,让enhance函数自动跳过DNS配置的加载 - log::info!(target: "app", "DNS settings disabled, regenerating config"); + logging!( + info, + Type::Config, + "DNS settings disabled, regenerating config" + ); // 重新生成配置 - if let Err(err) = Config::generate().await { - log::error!(target: "app", "Failed to regenerate config: {err}"); + if let Err(err) = Config::generate() { + logging!(error, Type::Config, "Failed to regenerate config: {err}"); return; } // 应用新配置 match CoreManager::global().update_config().await { Ok(_) => { - log::info!(target: "app", "Config regenerated successfully"); + logging!(info, Type::Config, "Config regenerated successfully"); handle::Handle::refresh_clash(); } Err(err) => { - log::error!(target: "app", "Failed to apply regenerated config: {err}"); + logging!( + error, + Type::Config, + "Failed to apply regenerated config: {err}" + ); } } } @@ -307,7 +327,10 @@ pub async fn get_clash_config() -> CmdResult { let key = ProxyRequestCache::make_key("clash_config", "default"); let value = cache .get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async { - manager.get_config().await.expect("fetch failed") + manager.get_config().await.unwrap_or_else(|e| { + logging!(error, Type::Cmd, "Failed to fetch clash config: {e}"); + serde_json::Value::Object(serde_json::Map::new()) + }) }) .await; Ok((*value).clone()) diff --git a/src-tauri/src/cmd/media_unlock_checker.rs b/src-tauri/src/cmd/media_unlock_checker.rs index 1b4c056b..e8661f1b 100644 --- a/src-tauri/src/cmd/media_unlock_checker.rs +++ b/src-tauri/src/cmd/media_unlock_checker.rs @@ -1,3 +1,4 @@ +use crate::{logging, utils::logging::Type}; use chrono::Local; use regex::Regex; use reqwest::Client; @@ -250,7 +251,23 @@ async fn check_gemini(client: &Client) -> UnlockItem { let status = if is_ok { "Yes" } else { "No" }; // 尝试提取国家代码 - let re = Regex::new(r#",2,1,200,"([A-Z]{3})""#).unwrap(); + let re = match Regex::new(r#",2,1,200,"([A-Z]{3})""#) { + Ok(re) => re, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile Gemini regex: {}", + e + ); + return UnlockItem { + name: "Gemini".to_string(), + status: "Failed".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let region = re.captures(&body).and_then(|caps| { caps.get(1).map(|m| { let country_code = m.as_str(); @@ -303,7 +320,23 @@ async fn check_youtube_premium(client: &Client) -> UnlockItem { } } else if body_lower.contains("ad-free") { // 尝试解析国家代码 - let re = Regex::new(r#"id="country-code"[^>]*>([^<]+)<"#).unwrap(); + let re = match Regex::new(r#"id="country-code"[^>]*>([^<]+)<"#) { + Ok(re) => re, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile YouTube Premium regex: {}", + e + ); + return UnlockItem { + name: "Youtube Premium".to_string(), + status: "Failed".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let region = re.captures(&body).and_then(|caps| { caps.get(1).map(|m| { let country_code = m.as_str().trim(); @@ -350,11 +383,16 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem { let cookie_store = Arc::new(reqwest::cookie::Jar::default()); // 使用带Cookie的客户端 - let client_with_cookies = reqwest::Client::builder() + let client_with_cookies = match reqwest::Client::builder() .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36") .cookie_provider(Arc::clone(&cookie_store)) - .build() - .unwrap_or_else(|_| client.clone()); + .build() { + Ok(client) => client, + Err(e) => { + logging!(error, Type::Network, "Failed to create client with cookies for Bahamut Anime: {}", e); + client.clone() + } + }; // 第一步:获取设备ID (会自动保存Cookie) let device_url = "https://ani.gamer.com.tw/ajax/getdeviceid.php"; @@ -363,10 +401,21 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem { match response.text().await { Ok(text) => { // 使用正则提取deviceid - let re = Regex::new(r#""deviceid"\s*:\s*"([^"]+)"#).unwrap(); - re.captures(&text) - .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) - .unwrap_or_default() + match Regex::new(r#""deviceid"\s*:\s*"([^"]+)"#) { + Ok(re) => re + .captures(&text) + .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) + .unwrap_or_default(), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile deviceid regex for Bahamut Anime: {}", + e + ); + String::new() + } + } } Err(_) => String::new(), } @@ -421,17 +470,25 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem { .await { Ok(response) => match response.text().await { - Ok(body) => { - let region_re = Regex::new(r#"data-geo="([^"]+)"#).unwrap(); - region_re + Ok(body) => match Regex::new(r#"data-geo="([^"]+)"#) { + Ok(region_re) => region_re .captures(&body) .and_then(|caps| caps.get(1)) .map(|m| { let country_code = m.as_str(); let emoji = country_code_to_emoji(country_code); format!("{emoji}{country_code}") - }) - } + }), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile region regex for Bahamut Anime: {}", + e + ); + None + } + }, Err(_) => None, }, Err(_) => None, @@ -495,8 +552,40 @@ async fn check_netflix(client: &Client) -> UnlockItem { } // 获取状态码 - let status1 = result1.unwrap().status().as_u16(); - let status2 = result2.unwrap().status().as_u16(); + let status1 = match result1 { + Ok(response) => response.status().as_u16(), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to get Netflix response 1: {}", + e + ); + return UnlockItem { + name: "Netflix".to_string(), + status: "Failed".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; + let status2 = match result2 { + Ok(response) => response.status().as_u16(), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to get Netflix response 2: {}", + e + ); + return UnlockItem { + name: "Netflix".to_string(), + status: "Failed".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; // 根据状态码判断解锁状况 if status1 == 404 && status2 == 404 { @@ -685,7 +774,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { }; } - let device_response = device_result.unwrap(); + let device_response = match device_result { + Ok(response) => response, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to get Disney+ device response: {}", + e + ); + return UnlockItem { + name: "Disney+".to_string(), + status: "Failed (Network Connection)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; // 检查是否 403 错误 if device_response.status().as_u16() == 403 { @@ -710,7 +815,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { }; // 提取 assertion - let re = Regex::new(r#""assertion"\s*:\s*"([^"]+)"#).unwrap(); + let re = match Regex::new(r#""assertion"\s*:\s*"([^"]+)"#) { + Ok(re) => re, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile assertion regex for Disney+: {}", + e + ); + return UnlockItem { + name: "Disney+".to_string(), + status: "Failed (Regex Error)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let assertion = match re.captures(&device_body) { Some(caps) => caps.get(1).map(|m| m.as_str().to_string()), None => None, @@ -729,7 +850,18 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { let token_url = "https://disney.api.edge.bamgrid.com/token"; // 构建请求体 - 使用表单数据格式而非 JSON - let assertion_str = assertion.unwrap(); + let assertion_str = match assertion { + Some(assertion) => assertion, + None => { + logging!(error, Type::Network, "No assertion found for Disney+"); + return UnlockItem { + name: "Disney+".to_string(), + status: "Failed (No Assertion)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let token_body = [ ( "grant_type", @@ -762,7 +894,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { }; } - let token_response = token_result.unwrap(); + let token_response = match token_result { + Ok(response) => response, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to get Disney+ token response: {}", + e + ); + return UnlockItem { + name: "Disney+".to_string(), + status: "Failed (Network Connection)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let token_status = token_response.status(); // 保存原始响应用于调试 @@ -798,10 +946,20 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { .map(|s| s.to_string()), Err(_) => { // 如果 JSON 解析失败,尝试使用正则表达式 - let refresh_token_re = Regex::new(r#""refresh_token"\s*:\s*"([^"]+)"#).unwrap(); - refresh_token_re - .captures(&token_body_text) - .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) + match Regex::new(r#""refresh_token"\s*:\s*"([^"]+)"#) { + Ok(refresh_token_re) => refresh_token_re + .captures(&token_body_text) + .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile refresh_token regex for Disney+: {}", + e + ); + None + } + } } }; @@ -825,7 +983,7 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { // GraphQL API 通常接受 JSON 格式 let graphql_payload = format!( r#"{{"query":"mutation refreshToken($input: RefreshTokenInput!) {{ refreshToken(refreshToken: $input) {{ activeSession {{ sessionId }} }} }}","variables":{{"input":{{"refreshToken":"{}"}}}}}}"#, - refresh_token.unwrap() + refresh_token.unwrap_or_default() ); let graphql_result = client @@ -857,21 +1015,56 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { }; // 解析 GraphQL 响应获取区域信息 - let graphql_response = graphql_result.unwrap(); + let graphql_response = match graphql_result { + Ok(response) => response, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to get Disney+ GraphQL response: {}", + e + ); + return UnlockItem { + name: "Disney+".to_string(), + status: "Failed (Network Connection)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let graphql_status = graphql_response.status(); - let graphql_body_text = (graphql_response.text().await).unwrap_or_default(); + let graphql_body_text = match graphql_response.text().await { + Ok(text) => text, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to read Disney+ GraphQL response text: {}", + e + ); + String::new() + } + }; // 如果 GraphQL 响应为空或明显错误,尝试直接获取区域信息 if graphql_body_text.is_empty() || graphql_status.as_u16() >= 400 { // 尝试直接从主页获取区域信息 let region_from_main = match client.get("https://www.disneyplus.com/").send().await { Ok(response) => match response.text().await { - Ok(body) => { - let region_re = Regex::new(r#"region"\s*:\s*"([^"]+)"#).unwrap(); - region_re + Ok(body) => match Regex::new(r#"region"\s*:\s*"([^"]+)"#) { + Ok(region_re) => region_re .captures(&body) - .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) - } + .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile Disney+ main page region regex: {}", + e + ); + None + } + }, Err(_) => None, }, Err(_) => None, @@ -898,28 +1091,59 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { region: None, check_time: Some(get_local_date_string()), }; - } else { + } + return UnlockItem { + name: "Disney+".to_string(), + status: format!( + "Failed (GraphQL error: {}, status: {})", + graphql_body_text.chars().take(50).collect::() + "...", + graphql_status.as_u16() + ), + region: None, + check_time: Some(get_local_date_string()), + }; + } + + // 提取国家代码 + let region_re = match Regex::new(r#""countryCode"\s*:\s*"([^"]+)"#) { + Ok(re) => re, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile Disney+ countryCode regex: {}", + e + ); return UnlockItem { name: "Disney+".to_string(), - status: format!( - "Failed (GraphQL error: {}, status: {})", - graphql_body_text.chars().take(50).collect::() + "...", - graphql_status.as_u16() - ), + status: "Failed (Regex Error)".to_string(), region: None, check_time: Some(get_local_date_string()), }; } - } - - // 提取国家代码 - let region_re = Regex::new(r#""countryCode"\s*:\s*"([^"]+)"#).unwrap(); + }; let region_code = region_re .captures(&graphql_body_text) .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())); // 提取支持状态 - let supported_re = Regex::new(r#""inSupportedLocation"\s*:\s*(false|true)"#).unwrap(); + let supported_re = match Regex::new(r#""inSupportedLocation"\s*:\s*(false|true)"#) { + Ok(re) => re, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile Disney+ supported location regex: {}", + e + ); + return UnlockItem { + name: "Disney+".to_string(), + status: "Failed (Regex Error)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let in_supported_location = supported_re .captures(&graphql_body_text) .and_then(|caps| caps.get(1).map(|m| m.as_str() == "true")); @@ -929,12 +1153,20 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { // 尝试直接从主页获取区域信息 let region_from_main = match client.get("https://www.disneyplus.com/").send().await { Ok(response) => match response.text().await { - Ok(body) => { - let region_re = Regex::new(r#"region"\s*:\s*"([^"]+)"#).unwrap(); - region_re + Ok(body) => match Regex::new(r#"region"\s*:\s*"([^"]+)"#) { + Ok(region_re) => region_re .captures(&body) - .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) - } + .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())), + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile Disney+ main page region regex: {}", + e + ); + None + } + }, Err(_) => None, }, Err(_) => None, @@ -958,7 +1190,18 @@ async fn check_disney_plus(client: &Client) -> UnlockItem { }; } - let region = region_code.unwrap(); + let region = match region_code { + Some(code) => code, + None => { + logging!(error, Type::Network, "No region code found for Disney+"); + return UnlockItem { + name: "Disney+".to_string(), + status: "No".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; // 判断日本地区 if region == "JP" { @@ -1028,13 +1271,47 @@ async fn check_prime_video(client: &Client) -> UnlockItem { } // 解析响应内容 - match result.unwrap().text().await { + let response = match result { + Ok(response) => response, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to get Prime Video response: {}", + e + ); + return UnlockItem { + name: "Prime Video".to_string(), + status: "Failed (Network Connection)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; + + match response.text().await { Ok(body) => { // 检查是否被地区限制 let is_blocked = body.contains("isServiceRestricted"); // 提取地区信息 - let region_re = Regex::new(r#""currentTerritory":"([^"]+)"#).unwrap(); + let region_re = match Regex::new(r#""currentTerritory":"([^"]+)"#) { + Ok(re) => re, + Err(e) => { + logging!( + error, + Type::Network, + "Failed to compile Prime Video region regex: {}", + e + ); + return UnlockItem { + name: "Prime Video".to_string(), + status: "Failed (Regex Error)".to_string(), + region: None, + check_time: Some(get_local_date_string()), + }; + } + }; let region_code = region_re .captures(&body) .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())); @@ -1182,8 +1459,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加哔哩哔哩大陆检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_bilibili_china_mainland(&client).await; let mut results = results.lock().await; @@ -1193,8 +1470,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加哔哩哔哩港澳台检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_bilibili_hk_mc_tw(&client).await; let mut results = results.lock().await; @@ -1204,8 +1481,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加合并的ChatGPT检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let chatgpt_results = check_chatgpt_combined(&client).await; let mut results = results.lock().await; @@ -1215,8 +1492,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加Gemini检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_gemini(&client).await; let mut results = results.lock().await; @@ -1226,8 +1503,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加YouTube Premium检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_youtube_premium(&client).await; let mut results = results.lock().await; @@ -1237,8 +1514,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加动画疯检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_bahamut_anime(&client).await; let mut results = results.lock().await; @@ -1248,8 +1525,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加 Netflix 检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_netflix(&client).await; let mut results = results.lock().await; @@ -1259,8 +1536,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加 Disney+ 检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_disney_plus(&client).await; let mut results = results.lock().await; @@ -1270,8 +1547,8 @@ pub async fn check_media_unlock() -> Result, String> { // 添加 Prime Video 检测任务 { - let client = client_arc.clone(); - let results = results.clone(); + let client = Arc::clone(&client_arc); + let results = Arc::clone(&results); tasks.spawn(async move { let result = check_prime_video(&client).await; let mut results = results.lock().await; @@ -1287,9 +1564,17 @@ pub async fn check_media_unlock() -> Result, String> { } // 获取所有结果 - let results = Arc::try_unwrap(results) - .expect("无法获取结果,可能仍有引用存在") - .into_inner(); + let results = match Arc::try_unwrap(results) { + Ok(mutex) => mutex.into_inner(), + Err(_) => { + logging!( + error, + Type::Network, + "Failed to unwrap results Arc, references still exist" + ); + return Err("Failed to collect results".to_string()); + } + }; Ok(results) } diff --git a/src-tauri/src/cmd/proxy.rs b/src-tauri/src/cmd/proxy.rs index 007118a9..1b72627f 100644 --- a/src-tauri/src/cmd/proxy.rs +++ b/src-tauri/src/cmd/proxy.rs @@ -1,5 +1,5 @@ use super::CmdResult; -use crate::{ipc::IpcManager, state::proxy::ProxyRequestCache}; +use crate::{ipc::IpcManager, logging, state::proxy::ProxyRequestCache, utils::logging::Type}; use std::time::Duration; const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60); @@ -12,7 +12,10 @@ pub async fn get_proxies() -> CmdResult { let key = ProxyRequestCache::make_key("proxies", "default"); let value = cache .get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async { - manager.get_proxies().await.expect("fetch failed") + manager.get_proxies().await.unwrap_or_else(|e| { + logging!(error, Type::Cmd, "Failed to fetch proxies: {e}"); + serde_json::Value::Object(serde_json::Map::new()) + }) }) .await; Ok((*value).clone()) @@ -34,7 +37,10 @@ pub async fn get_providers_proxies() -> CmdResult { let key = ProxyRequestCache::make_key("providers", "default"); let value = cache .get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async { - manager.get_providers_proxies().await.expect("fetch failed") + manager.get_providers_proxies().await.unwrap_or_else(|e| { + logging!(error, Type::Cmd, "Failed to fetch provider proxies: {e}"); + serde_json::Value::Object(serde_json::Map::new()) + }) }) .await; Ok((*value).clone()) diff --git a/src-tauri/src/cmd/save_profile.rs b/src-tauri/src/cmd/save_profile.rs index 9d9bdfa0..4b264e17 100644 --- a/src-tauri/src/cmd/save_profile.rs +++ b/src-tauri/src/cmd/save_profile.rs @@ -29,7 +29,8 @@ pub async fn save_profile_file(index: String, file_data: Option) -> CmdR }; // 保存新的配置文件 - wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?; + let file_data = file_data.ok_or("file_data is None")?; + wrap_err!(fs::write(&file_path, &file_data))?; let file_path_str = file_path.to_string_lossy().to_string(); logging!( @@ -139,17 +140,29 @@ pub async fn save_profile_file(index: String, file_data: Option) -> CmdR || (!file_path_str.ends_with(".js") && !is_script_error) { // 普通YAML错误使用YAML通知处理 - log::info!(target: "app", "[cmd配置save] YAML配置文件验证失败,发送通知"); + logging!( + info, + Type::Config, + "[cmd配置save] YAML配置文件验证失败,发送通知" + ); let result = (false, error_msg.clone()); crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件"); } else if is_script_error { // 脚本错误使用专门的通知处理 - log::info!(target: "app", "[cmd配置save] 脚本文件验证失败,发送通知"); + logging!( + info, + Type::Config, + "[cmd配置save] 脚本文件验证失败,发送通知" + ); let result = (false, error_msg.clone()); crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件"); } else { // 普通配置错误使用一般通知 - log::info!(target: "app", "[cmd配置save] 其他类型验证失败,发送一般通知"); + logging!( + info, + Type::Config, + "[cmd配置save] 其他类型验证失败,发送一般通知" + ); handle::Handle::notice_message("config_validate::error", &error_msg); } diff --git a/src-tauri/src/cmd/service.rs b/src-tauri/src/cmd/service.rs index c0f5b2fc..7326986e 100644 --- a/src-tauri/src/cmd/service.rs +++ b/src-tauri/src/cmd/service.rs @@ -3,13 +3,15 @@ use crate::{ core::{service, CoreManager}, utils::i18n::t, }; +use anyhow::Result; -async fn execute_service_operation( - service_op: impl std::future::Future>, - op_type: &str, -) -> CmdResult { - if service_op.await.is_err() { - let emsg = format!("{} {} failed", op_type, "Service"); +async fn execute_service_operation_sync(service_op: F, op_type: &str) -> CmdResult +where + F: FnOnce() -> Result<(), E>, + E: ToString + std::fmt::Debug, +{ + if let Err(e) = service_op() { + let emsg = format!("{} {} failed: {}", op_type, "Service", e.to_string()); return Err(t(emsg.as_str())); } if CoreManager::global().restart_core().await.is_err() { @@ -21,22 +23,22 @@ async fn execute_service_operation( #[tauri::command] pub async fn install_service() -> CmdResult { - execute_service_operation(service::install_service(), "Install").await + execute_service_operation_sync(service::install_service, "Install").await } #[tauri::command] pub async fn uninstall_service() -> CmdResult { - execute_service_operation(service::uninstall_service(), "Uninstall").await + execute_service_operation_sync(service::uninstall_service, "Uninstall").await } #[tauri::command] pub async fn reinstall_service() -> CmdResult { - execute_service_operation(service::reinstall_service(), "Reinstall").await + execute_service_operation_sync(service::reinstall_service, "Reinstall").await } #[tauri::command] pub async fn repair_service() -> CmdResult { - execute_service_operation(service::force_reinstall_service(), "Repair").await + execute_service_operation_sync(service::force_reinstall_service, "Repair").await } #[tauri::command] diff --git a/src-tauri/src/cmd/system.rs b/src-tauri/src/cmd/system.rs index 6dcc312a..a1807244 100644 --- a/src-tauri/src/cmd/system.rs +++ b/src-tauri/src/cmd/system.rs @@ -1,7 +1,9 @@ use super::CmdResult; use crate::{ core::{handle, CoreManager}, + logging, module::sysinfo::PlatformSpecification, + utils::logging::Type, }; use once_cell::sync::Lazy; use std::{ @@ -23,20 +25,22 @@ static APP_START_TIME: Lazy = Lazy::new(|| { #[tauri::command] pub async fn export_diagnostic_info() -> CmdResult<()> { - let sysinfo = PlatformSpecification::new_async().await; + let sysinfo = PlatformSpecification::new_sync(); let info = format!("{sysinfo:?}"); - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = handle::Handle::global() + .app_handle() + .ok_or("Failed to get app handle")?; let cliboard = app_handle.clipboard(); if cliboard.write_text(info).is_err() { - log::error!(target: "app", "Failed to write to clipboard"); + logging!(error, Type::System, "Failed to write to clipboard"); } Ok(()) } #[tauri::command] pub async fn get_system_info() -> CmdResult { - let sysinfo = PlatformSpecification::new_async().await; + let sysinfo = PlatformSpecification::new_sync(); let info = format!("{sysinfo:?}"); Ok(info) } @@ -44,7 +48,7 @@ pub async fn get_system_info() -> CmdResult { /// 获取当前内核运行模式 #[tauri::command] pub async fn get_running_mode() -> Result { - Ok(CoreManager::global().get_running_mode().await.to_string()) + Ok(CoreManager::global().get_running_mode().to_string()) } /// 获取应用的运行时间(毫秒) diff --git a/src-tauri/src/cmd/uwp.rs b/src-tauri/src/cmd/uwp.rs index 28b88bd4..dc2eb120 100644 --- a/src-tauri/src/cmd/uwp.rs +++ b/src-tauri/src/cmd/uwp.rs @@ -6,8 +6,8 @@ mod platform { use super::CmdResult; use crate::{core::win_uwp, wrap_err}; - pub async fn invoke_uwp_tool() -> CmdResult { - wrap_err!(win_uwp::invoke_uwptools().await) + pub fn invoke_uwp_tool() -> CmdResult { + wrap_err!(win_uwp::invoke_uwptools()) } } @@ -16,7 +16,7 @@ mod platform { mod platform { use super::CmdResult; - pub async fn invoke_uwp_tool() -> CmdResult { + pub fn invoke_uwp_tool() -> CmdResult { Ok(()) } } @@ -24,5 +24,5 @@ mod platform { /// Command exposed to Tauri #[tauri::command] pub async fn invoke_uwp_tool() -> CmdResult { - platform::invoke_uwp_tool().await + platform::invoke_uwp_tool() } diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index 81a91d34..dc8e92a2 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -19,7 +19,9 @@ impl IClashTemp { Ok(mut map) => { template.0.keys().for_each(|key| { if !map.contains_key(key) { - map.insert(key.clone(), template.0.get(key).unwrap().clone()); + if let Some(value) = template.0.get(key) { + map.insert(key.clone(), value.clone()); + } } }); // 确保 secret 字段存在且不为空 @@ -307,7 +309,13 @@ impl IClashTemp { pub fn guard_external_controller_ipc() -> String { // 总是使用当前的 IPC 路径,确保配置文件与运行时路径一致 - path_to_str(&ipc_path().unwrap()).unwrap().to_string() + ipc_path() + .ok() + .and_then(|path| path_to_str(&path).ok().map(|s| s.to_string())) + .unwrap_or_else(|| { + log::error!(target: "app", "Failed to get IPC path, using default"); + "127.0.0.1:9090".to_string() + }) } } diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index ef462087..cc1a1ee2 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -72,7 +72,7 @@ impl Config { .append_item(script_item.clone())?; } // 生成运行时配置 - if let Err(err) = Self::generate().await { + if let Err(err) = Self::generate() { logging!(error, Type::Config, true, "生成运行时配置失败: {}", err); } else { logging!(info, Type::Config, true, "生成运行时配置成功"); @@ -96,8 +96,7 @@ impl Config { error_msg ); CoreManager::global() - .use_default_config("config_validate::boot_error", &error_msg) - .await?; + .use_default_config("config_validate::boot_error", &error_msg)?; Some(("config_validate::boot_error", error_msg)) } else { logging!(info, Type::Config, true, "配置验证成功"); @@ -107,16 +106,13 @@ impl Config { Err(err) => { logging!(warn, Type::Config, true, "验证进程执行失败: {}", err); CoreManager::global() - .use_default_config("config_validate::process_terminated", "") - .await?; + .use_default_config("config_validate::process_terminated", "")?; Some(("config_validate::process_terminated", String::new())) } } } else { logging!(warn, Type::Config, true, "生成配置文件失败,使用默认配置"); - CoreManager::global() - .use_default_config("config_validate::error", "") - .await?; + CoreManager::global().use_default_config("config_validate::error", "")?; Some(("config_validate::error", String::new())) }; @@ -150,8 +146,8 @@ impl Config { } /// 生成订阅存好 - pub async fn generate() -> Result<()> { - let (config, exists_keys, logs) = enhance::enhance().await; + pub fn generate() -> Result<()> { + let (config, exists_keys, logs) = enhance::enhance(); *Config::runtime().draft_mut() = Box::new(IRuntime { config: Some(config), @@ -174,33 +170,33 @@ mod tests { use std::mem; #[test] + #[allow(unused_variables)] + #[allow(clippy::expect_used)] fn test_prfitem_from_merge_size() { - let merge_item = PrfItem::from_merge(Some("Merge".to_string())).unwrap(); - dbg!(&merge_item); + let merge_item = PrfItem::from_merge(Some("Merge".to_string())) + .expect("Failed to create merge item in test"); let prfitem_size = mem::size_of_val(&merge_item); - dbg!(prfitem_size); // Boxed version let boxed_merge_item = Box::new(merge_item); let box_prfitem_size = mem::size_of_val(&boxed_merge_item); - dbg!(box_prfitem_size); // The size of Box is always pointer-sized (usually 8 bytes on 64-bit) // assert_eq!(box_prfitem_size, mem::size_of::>()); assert!(box_prfitem_size < prfitem_size); } #[test] + #[allow(unused_variables)] fn test_draft_size_non_boxed() { let draft = Draft::from(IRuntime::new()); let iruntime_size = std::mem::size_of_val(&draft); - dbg!(iruntime_size); assert_eq!(iruntime_size, std::mem::size_of::>()); } #[test] + #[allow(unused_variables)] fn test_draft_size_boxed() { let draft = Draft::from(Box::new(IRuntime::new())); let box_iruntime_size = std::mem::size_of_val(&draft); - dbg!(box_iruntime_size); assert_eq!( box_iruntime_size, std::mem::size_of::>>() diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 041c79e6..ac82d685 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -46,11 +46,18 @@ impl Draft> { if guard.1.is_none() { let mut guard = RwLockUpgradableReadGuard::upgrade(guard); guard.1 = Some(guard.0.clone()); - return RwLockWriteGuard::map(guard, |inner| inner.1.as_mut().unwrap()); + return RwLockWriteGuard::map(guard, |inner| { + inner.1.as_mut().unwrap_or_else(|| { + unreachable!("Draft was just created above, this should never fail") + }) + }); } // 已存在草稿,升级为写锁映射 RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| { - inner.1.as_mut().unwrap() + inner + .1 + .as_mut() + .unwrap_or_else(|| unreachable!("Draft should exist when guard.1.is_some()")) }) } diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index a526cc56..88053dc2 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -147,12 +147,15 @@ impl PrfItem { bail!("type should not be null"); } - match item.itype.unwrap().as_str() { + let itype = item + .itype + .ok_or_else(|| anyhow::anyhow!("type should not be null"))?; + match itype.as_str() { "remote" => { - if item.url.is_none() { - bail!("url should not be null"); - } - let url = item.url.as_ref().unwrap().as_str(); + let url = item + .url + .as_ref() + .ok_or_else(|| anyhow::anyhow!("url should not be null"))?; let name = item.name; let desc = item.desc; PrfItem::from_url(url, name, desc, item.option).await @@ -549,22 +552,20 @@ impl PrfItem { /// get the file data pub fn read_file(&self) -> Result { - if self.file.is_none() { - bail!("could not find the file"); - } - - let file = self.file.clone().unwrap(); + let file = self + .file + .clone() + .ok_or_else(|| anyhow::anyhow!("could not find the file"))?; let path = dirs::app_profiles_dir()?.join(file); fs::read_to_string(path).context("failed to read the file") } /// save the file data pub fn save_file(&self, data: String) -> Result<()> { - if self.file.is_none() { - bail!("could not find the file"); - } - - let file = self.file.clone().unwrap(); + let file = self + .file + .clone() + .ok_or_else(|| anyhow::anyhow!("could not find the file"))?; let path = dirs::app_profiles_dir()?.join(file); fs::write(path, data.as_bytes()).context("failed to save the file") } diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index c717065b..ce4df733 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -77,10 +77,11 @@ impl IProfiles { } if let Some(current) = patch.current { - let items = self.items.as_ref().unwrap(); - let some_uid = Some(current); - if items.iter().any(|e| e.uid == some_uid) { - self.current = some_uid; + if let Some(items) = self.items.as_ref() { + let some_uid = Some(current); + if items.iter().any(|e| e.uid == some_uid) { + self.current = some_uid; + } } } @@ -127,7 +128,9 @@ impl IProfiles { bail!("the file should not be null"); } - let file = item.file.clone().unwrap(); + let file = item.file.clone().ok_or_else(|| { + anyhow::anyhow!("file field is required when file_data is provided") + })?; let path = dirs::app_profiles_dir()?.join(&file); fs::File::create(path) @@ -168,11 +171,12 @@ impl IProfiles { } } - if old_index.is_none() || new_index.is_none() { - return Ok(()); - } - let item = items.remove(old_index.unwrap()); - items.insert(new_index.unwrap(), item); + let (old_idx, new_idx) = match (old_index, new_index) { + (Some(old), Some(new)) => (old, new), + _ => return Ok(()), + }; + let item = items.remove(old_idx); + items.insert(new_idx, item); self.items = Some(items); self.save_file() } diff --git a/src-tauri/src/core/backup.rs b/src-tauri/src/core/backup.rs index 47052eef..0f618957 100644 --- a/src-tauri/src/core/backup.rs +++ b/src-tauri/src/core/backup.rs @@ -117,8 +117,7 @@ impl WebDavClient { attempt.follow() } })) - .build() - .unwrap(), + .build()?, ) .set_host(config.url) .set_auth(reqwest_dav::Auth::Basic(config.username, config.password)) @@ -243,12 +242,17 @@ pub fn create_backup() -> Result<(String, PathBuf), Error> { let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored); if let Ok(entries) = fs::read_dir(dirs::app_profiles_dir()?) { for entry in entries { - let entry = entry.unwrap(); + let entry = entry?; let path = entry.path(); if path.is_file() { - let backup_path = format!("profiles/{}", entry.file_name().to_str().unwrap()); + let file_name_os = entry.file_name(); + let file_name = file_name_os + .to_str() + .ok_or_else(|| anyhow::Error::msg("Invalid file name encoding"))?; + let backup_path = format!("profiles/{}", file_name); zip.start_file(backup_path, options)?; - zip.write_all(fs::read(path).unwrap().as_slice())?; + let file_content = fs::read(&path)?; + zip.write_all(&file_content)?; } } } diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 02f4ed26..92b8a622 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -14,6 +14,7 @@ use crate::{ }; use anyhow::Result; use chrono::Local; +use parking_lot::Mutex; use std::{ fmt, fs::{create_dir_all, File}, @@ -22,7 +23,6 @@ use std::{ sync::Arc, }; use tauri_plugin_shell::{process::CommandChild, ShellExt}; -use tokio::sync::Mutex; #[derive(Debug)] pub struct CoreManager { @@ -135,7 +135,7 @@ impl CoreManager { Ok(false) } /// 使用默认配置 - pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> { + pub fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> { let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG); *Config::runtime().draft_mut() = Box::new(IRuntime { config: Some(Config::clash().latest_ref().0.clone()), @@ -185,7 +185,7 @@ impl CoreManager { "检测到Merge文件,仅进行语法检查: {}", config_path ); - return self.validate_file_syntax(config_path).await; + return self.validate_file_syntax(config_path); } // 检查是否为脚本文件 @@ -217,7 +217,7 @@ impl CoreManager { "检测到脚本文件,使用JavaScript验证: {}", config_path ); - return self.validate_script_file(config_path).await; + return self.validate_script_file(config_path); } // 对YAML配置文件使用Clash内核验证 @@ -249,7 +249,11 @@ impl CoreManager { let clash_core = Config::verge().latest_ref().get_valid_clash_core(); logging!(info, Type::Config, true, "使用内核: {}", clash_core); - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = handle::Handle::global().app_handle().ok_or_else(|| { + let msg = "Failed to get app handle"; + logging!(error, Type::Core, true, "{}", msg); + anyhow::anyhow!(msg) + })?; let app_dir = dirs::app_home_dir()?; let app_dir_str = dirs::path_to_str(&app_dir)?; logging!(info, Type::Config, true, "验证目录: {}", app_dir_str); @@ -297,7 +301,7 @@ impl CoreManager { } } /// 只进行文件语法检查,不进行完整验证 - async fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> { + fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> { logging!(info, Type::Config, true, "开始检查文件: {}", config_path); // 读取文件内容 @@ -325,7 +329,7 @@ impl CoreManager { } } /// 验证脚本文件语法 - async fn validate_script_file(&self, path: &str) -> Result<(bool, String)> { + fn validate_script_file(&self, path: &str) -> Result<(bool, String)> { // 读取脚本内容 let content = match std::fs::read_to_string(path) { Ok(content) => content, @@ -382,7 +386,7 @@ impl CoreManager { // 1. 先生成新的配置内容 logging!(info, Type::Config, true, "生成新的配置内容"); - Config::generate().await?; + Config::generate()?; // 2. 验证配置 match self.validate_config().await { @@ -435,7 +439,7 @@ impl CoreManager { // 获取当前管理的进程 PID let current_pid = { - let child_guard = self.child_sidecar.lock().await; + let child_guard = self.child_sidecar.lock(); child_guard.as_ref().map(|child| child.pid()) }; @@ -729,7 +733,7 @@ impl CoreManager { } } - async fn start_core_by_sidecar(&self) -> Result<()> { + fn start_core_by_sidecar(&self) -> Result<()> { logging!(trace, Type::Core, true, "Running core by sidecar"); let config_file = &Config::generate_file(ConfigType::Run)?; let app_handle = handle::Handle::global() @@ -783,14 +787,14 @@ impl CoreManager { "Started core by sidecar pid: {}", pid ); - *self.child_sidecar.lock().await = Some(child); - self.set_running_mode(RunningMode::Sidecar).await; + *self.child_sidecar.lock() = Some(child); + self.set_running_mode(RunningMode::Sidecar); Ok(()) } - async fn stop_core_by_sidecar(&self) -> Result<()> { + fn stop_core_by_sidecar(&self) -> Result<()> { logging!(trace, Type::Core, true, "Stopping core by sidecar"); - if let Some(child) = self.child_sidecar.lock().await.take() { + if let Some(child) = self.child_sidecar.lock().take() { let pid = child.pid(); child.kill()?; logging!( @@ -801,7 +805,7 @@ impl CoreManager { pid ); } - self.set_running_mode(RunningMode::NotRunning).await; + self.set_running_mode(RunningMode::NotRunning); Ok(()) } } @@ -811,13 +815,13 @@ impl CoreManager { logging!(trace, Type::Core, true, "Running core by service"); let config_file = &Config::generate_file(ConfigType::Run)?; service::run_core_by_service(config_file).await?; - self.set_running_mode(RunningMode::Service).await; + self.set_running_mode(RunningMode::Service); Ok(()) } async fn stop_core_by_service(&self) -> Result<()> { logging!(trace, Type::Core, true, "Stopping core by service"); service::stop_core_by_service().await?; - self.set_running_mode(RunningMode::NotRunning).await; + self.set_running_mode(RunningMode::NotRunning); Ok(()) } } @@ -839,7 +843,7 @@ impl CoreManager { async fn attempt_service_init(&self) -> Result<()> { if service::check_service_needs_reinstall().await { logging!(info, Type::Core, true, "服务版本不匹配或状态异常,执行重装"); - if let Err(e) = service::reinstall_service().await { + if let Err(e) = service::reinstall_service() { logging!( warn, Type::Core, @@ -944,7 +948,7 @@ impl CoreManager { true, "用户偏好Sidecar模式或先前服务启动失败,使用Sidecar模式启动" ); - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; // 如果 sidecar 启动成功,我们可以认为核心初始化流程到此结束 // 后续的 Tray::global().subscribe_traffic().await 仍然会执行 } else { @@ -956,7 +960,7 @@ impl CoreManager { true, "无服务安装记录 (首次运行或状态重置),尝试安装服务" ); - match service::install_service().await { + match service::install_service() { Ok(_) => { logging!(info, Type::Core, true, "服务安装成功(首次尝试)"); let mut new_state = service::ServiceState::default(); @@ -980,7 +984,7 @@ impl CoreManager { final_state.last_error = Some("Newly installed service failed to start".to_string()); final_state.save()?; - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; } } else { logging!( @@ -996,7 +1000,7 @@ impl CoreManager { .to_string(), ); final_state.save()?; - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; } } Err(err) => { @@ -1007,7 +1011,7 @@ impl CoreManager { ..Default::default() }; new_state.save()?; - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; } } } else { @@ -1036,7 +1040,7 @@ impl CoreManager { })); final_state.save()?; } - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; } } } @@ -1047,13 +1051,13 @@ impl CoreManager { Ok(()) } - pub async fn set_running_mode(&self, mode: RunningMode) { - let mut guard = self.running.lock().await; + pub fn set_running_mode(&self, mode: RunningMode) { + let mut guard = self.running.lock(); *guard = mode; } - pub async fn get_running_mode(&self) -> RunningMode { - let guard = self.running.lock().await; + pub fn get_running_mode(&self) -> RunningMode { + let guard = self.running.lock(); (*guard).clone() } @@ -1061,7 +1065,7 @@ impl CoreManager { pub async fn start_core(&self) -> Result<()> { if service::is_service_available().await.is_ok() { if service::check_service_needs_reinstall().await { - service::reinstall_service().await?; + service::reinstall_service()?; } logging!(info, Type::Core, true, "服务可用,使用服务模式启动"); self.start_core_by_service().await?; @@ -1075,10 +1079,10 @@ impl CoreManager { true, "服务不可用,根据用户偏好使用Sidecar模式" ); - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; } else { logging!(info, Type::Core, true, "服务不可用,使用Sidecar模式"); - self.start_core_by_sidecar().await?; + self.start_core_by_sidecar()?; } } Ok(()) @@ -1086,9 +1090,9 @@ impl CoreManager { /// 停止核心运行 pub async fn stop_core(&self) -> Result<()> { - match self.get_running_mode().await { + match self.get_running_mode() { RunningMode::Service => self.stop_core_by_service().await, - RunningMode::Sidecar => self.stop_core_by_sidecar().await, + RunningMode::Sidecar => self.stop_core_by_sidecar(), RunningMode::NotRunning => Ok(()), } } @@ -1108,8 +1112,12 @@ impl CoreManager { logging!(error, Type::Core, true, "{}", error_message); return Err(error_message.to_string()); } - let core: &str = &clash_core.clone().unwrap(); - if !IVerge::VALID_CLASH_CORES.contains(&core) { + let core = clash_core.as_ref().ok_or_else(|| { + let msg = "Clash core should not be None"; + logging!(error, Type::Core, true, "{}", msg); + msg.to_string() + })?; + if !IVerge::VALID_CLASH_CORES.contains(&core.as_str()) { let error_message = format!("Clash core invalid name: {core}"); logging!(error, Type::Core, true, "{}", error_message); return Err(error_message); diff --git a/src-tauri/src/core/event_driven_proxy.rs b/src-tauri/src/core/event_driven_proxy.rs index 52370ce9..4ad85247 100644 --- a/src-tauri/src/core/event_driven_proxy.rs +++ b/src-tauri/src/core/event_driven_proxy.rs @@ -97,7 +97,7 @@ impl EventDrivenProxyManager { let (event_tx, event_rx) = mpsc::unbounded_channel(); let (query_tx, query_rx) = mpsc::unbounded_channel(); - Self::start_event_loop(state.clone(), event_rx, query_rx); + Self::start_event_loop(Arc::clone(&state), event_rx, query_rx); Self { state, @@ -218,7 +218,7 @@ impl EventDrivenProxyManager { Self::enable_system_proxy(state).await; } ProxyEvent::DisableProxy => { - Self::disable_system_proxy(state).await; + Self::disable_system_proxy(state); } ProxyEvent::SwitchToPac => { Self::switch_proxy_mode(state, true).await; @@ -307,7 +307,9 @@ impl EventDrivenProxyManager { if !current.enable || current.url != expected.url { log::info!(target: "app", "PAC代理设置异常,正在恢复..."); - Self::restore_pac_proxy(&expected.url).await; + if let Err(e) = Self::restore_pac_proxy(&expected.url).await { + log::error!(target: "app", "恢复PAC代理失败: {}", e); + } sleep(Duration::from_millis(500)).await; let restored = Self::get_auto_proxy_with_timeout().await; @@ -329,7 +331,9 @@ impl EventDrivenProxyManager { if !current.enable || current.host != expected.host || current.port != expected.port { log::info!(target: "app", "系统代理设置异常,正在恢复..."); - Self::restore_sys_proxy(&expected).await; + if let Err(e) = Self::restore_sys_proxy(&expected).await { + log::error!(target: "app", "恢复系统代理失败: {}", e); + } sleep(Duration::from_millis(500)).await; let restored = Self::get_sys_proxy_with_timeout().await; @@ -350,16 +354,20 @@ impl EventDrivenProxyManager { if pac_enabled { let expected = Self::get_expected_pac_config(); - Self::restore_pac_proxy(&expected.url).await; + if let Err(e) = Self::restore_pac_proxy(&expected.url).await { + log::error!(target: "app", "启用PAC代理失败: {}", e); + } } else { let expected = Self::get_expected_sys_proxy(); - Self::restore_sys_proxy(&expected).await; + if let Err(e) = Self::restore_sys_proxy(&expected).await { + log::error!(target: "app", "启用系统代理失败: {}", e); + } } Self::check_and_restore_proxy(state).await; } - async fn disable_system_proxy(_state: &Arc>) { + fn disable_system_proxy(_state: &Arc>) { log::info!(target: "app", "禁用系统代理"); #[cfg(not(target_os = "windows"))] @@ -380,13 +388,17 @@ impl EventDrivenProxyManager { logging_error!(Type::System, true, disabled_sys.set_system_proxy()); let expected = Self::get_expected_pac_config(); - Self::restore_pac_proxy(&expected.url).await; + if let Err(e) = Self::restore_pac_proxy(&expected.url).await { + log::error!(target: "app", "切换到PAC模式失败: {}", e); + } } else { let disabled_auto = Autoproxy::default(); logging_error!(Type::System, true, disabled_auto.set_auto_proxy()); let expected = Self::get_expected_sys_proxy(); - Self::restore_sys_proxy(&expected).await; + if let Err(e) = Self::restore_sys_proxy(&expected).await { + log::error!(target: "app", "切换到HTTP代理模式失败: {}", e); + } } Self::update_state_timestamp(state, |s| s.pac_enabled = to_pac); @@ -506,37 +518,45 @@ impl EventDrivenProxyManager { } } - async fn restore_pac_proxy(expected_url: &str) { - #[cfg(not(target_os = "windows"))] + #[cfg(target_os = "windows")] + async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> { + Self::execute_sysproxy_command(&["pac", expected_url]).await + } + + #[allow(clippy::unused_async)] + #[cfg(not(target_os = "windows"))] + async fn restore_pac_proxy(expected_url: &str) -> Result<(), anyhow::Error> { { let new_autoproxy = Autoproxy { enable: true, url: expected_url.to_string(), }; - logging_error!(Type::System, true, new_autoproxy.set_auto_proxy()); - } - - #[cfg(target_os = "windows")] - { - Self::execute_sysproxy_command(&["pac", expected_url]).await; - } - } - - async fn restore_sys_proxy(expected: &Sysproxy) { - #[cfg(not(target_os = "windows"))] - { - logging_error!(Type::System, true, expected.set_system_proxy()); - } - - #[cfg(target_os = "windows")] - { - let address = format!("{}:{}", expected.host, expected.port); - Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await; + // logging_error!(Type::System, true, new_autoproxy.set_auto_proxy()); + new_autoproxy + .set_auto_proxy() + .map_err(|e| anyhow::anyhow!("Failed to set auto proxy: {}", e)) } } #[cfg(target_os = "windows")] - async fn execute_sysproxy_command(args: &[&str]) { + async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> { + let address = format!("{}:{}", expected.host, expected.port); + Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await + } + + #[allow(clippy::unused_async)] + #[cfg(not(target_os = "windows"))] + async fn restore_sys_proxy(expected: &Sysproxy) -> Result<(), anyhow::Error> { + { + // logging_error!(Type::System, true, expected.set_system_proxy()); + expected + .set_system_proxy() + .map_err(|e| anyhow::anyhow!("Failed to set system proxy: {}", e)) + } + } + + #[cfg(target_os = "windows")] + async fn execute_sysproxy_command(args: &[&str]) -> Result<(), anyhow::Error> { use crate::utils::dirs; #[allow(unused_imports)] // creation_flags必须 use std::os::windows::process::CommandExt; @@ -546,37 +566,22 @@ impl EventDrivenProxyManager { Ok(path) => path, Err(e) => { log::error!(target: "app", "获取服务路径失败: {e}"); - return; + return Err(e); } }; let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); if !sysproxy_exe.exists() { log::error!(target: "app", "sysproxy.exe 不存在"); - return; } + anyhow::ensure!(sysproxy_exe.exists(), "sysproxy.exe does not exist"); - let output = Command::new(sysproxy_exe) + let _output = Command::new(sysproxy_exe) .args(args) .creation_flags(0x08000000) // CREATE_NO_WINDOW - 隐藏窗口 .output() - .await; + .await?; - match output { - Ok(output) => { - if !output.status.success() { - log::error!(target: "app", "执行sysproxy命令失败: {args:?}"); - let stderr = String::from_utf8_lossy(&output.stderr); - if !stderr.is_empty() { - log::error!(target: "app", "sysproxy错误输出: {stderr}"); - } - } else { - log::debug!(target: "app", "成功执行sysproxy命令: {args:?}"); - } - } - Err(e) => { - log::error!(target: "app", "执行sysproxy命令出错: {e}"); - } - } + Ok(()) } } diff --git a/src-tauri/src/core/handle.rs b/src-tauri/src/core/handle.rs index 5ded1885..820dce99 100644 --- a/src-tauri/src/core/handle.rs +++ b/src-tauri/src/core/handle.rs @@ -82,121 +82,123 @@ impl NotificationSystem { *self.last_emit_time.write() = Instant::now(); - self.worker_handle = Some( - thread::Builder::new() - .name("frontend-notifier".into()) - .spawn(move || { - let handle = Handle::global(); + match thread::Builder::new() + .name("frontend-notifier".into()) + .spawn(move || { + let handle = Handle::global(); - while !handle.is_exiting() { - match rx.recv_timeout(Duration::from_millis(100)) { - Ok(event) => { - let system_guard = handle.notification_system.read(); - if system_guard.as_ref().is_none() { - log::warn!("NotificationSystem not found in handle while processing event."); - continue; - } - let system = system_guard.as_ref().unwrap(); + while !handle.is_exiting() { + match rx.recv_timeout(Duration::from_millis(100)) { + Ok(event) => { + let system_guard = handle.notification_system.read(); + let Some(system) = system_guard.as_ref() else { + log::warn!("NotificationSystem not found in handle while processing event."); + continue; + }; - let is_emergency = *system.emergency_mode.read(); + let is_emergency = *system.emergency_mode.read(); - if is_emergency { - if let FrontendEvent::NoticeMessage { ref status, .. } = event { - if status == "info" { - log::warn!( - "Emergency mode active, skipping info message" - ); - continue; - } + if is_emergency { + if let FrontendEvent::NoticeMessage { ref status, .. } = event { + if status == "info" { + log::warn!( + "Emergency mode active, skipping info message" + ); + continue; } } + } - if let Some(window) = handle.get_window() { - *system.last_emit_time.write() = Instant::now(); + if let Some(window) = handle.get_window() { + *system.last_emit_time.write() = Instant::now(); - let (event_name_str, payload_result) = match event { - FrontendEvent::RefreshClash => { - ("verge://refresh-clash-config", Ok(serde_json::json!("yes"))) - } - FrontendEvent::RefreshVerge => { - ("verge://refresh-verge-config", Ok(serde_json::json!("yes"))) - } - FrontendEvent::NoticeMessage { status, message } => { - match serde_json::to_value((status, message)) { - Ok(p) => ("verge://notice-message", Ok(p)), - Err(e) => { - log::error!("Failed to serialize NoticeMessage payload: {e}"); - ("verge://notice-message", Err(e)) - } - } - } - FrontendEvent::ProfileChanged { current_profile_id } => { - ("profile-changed", Ok(serde_json::json!(current_profile_id))) - } - FrontendEvent::TimerUpdated { profile_index } => { - ("verge://timer-updated", Ok(serde_json::json!(profile_index))) - } - FrontendEvent::StartupCompleted => { - ("verge://startup-completed", Ok(serde_json::json!(null))) - } - FrontendEvent::ProfileUpdateStarted { uid } => { - ("profile-update-started", Ok(serde_json::json!({ "uid": uid }))) - } - FrontendEvent::ProfileUpdateCompleted { uid } => { - ("profile-update-completed", Ok(serde_json::json!({ "uid": uid }))) - } - }; - - if let Ok(payload) = payload_result { - match window.emit(event_name_str, payload) { - Ok(_) => { - system.stats.total_sent.fetch_add(1, Ordering::SeqCst); - // 记录成功发送的事件 - if log::log_enabled!(log::Level::Debug) { - log::debug!("Successfully emitted event: {event_name_str}"); - } - } + let (event_name_str, payload_result) = match event { + FrontendEvent::RefreshClash => { + ("verge://refresh-clash-config", Ok(serde_json::json!("yes"))) + } + FrontendEvent::RefreshVerge => { + ("verge://refresh-verge-config", Ok(serde_json::json!("yes"))) + } + FrontendEvent::NoticeMessage { status, message } => { + match serde_json::to_value((status, message)) { + Ok(p) => ("verge://notice-message", Ok(p)), Err(e) => { - log::warn!("Failed to emit event {event_name_str}: {e}"); - system.stats.total_errors.fetch_add(1, Ordering::SeqCst); - *system.stats.last_error_time.write() = Some(Instant::now()); - - let errors = system.stats.total_errors.load(Ordering::SeqCst); - const EMIT_ERROR_THRESHOLD: u64 = 10; - if errors > EMIT_ERROR_THRESHOLD && !*system.emergency_mode.read() { - log::warn!( - "Reached {EMIT_ERROR_THRESHOLD} emit errors, entering emergency mode" - ); - *system.emergency_mode.write() = true; - } + log::error!("Failed to serialize NoticeMessage payload: {e}"); + ("verge://notice-message", Err(e)) + } + } + } + FrontendEvent::ProfileChanged { current_profile_id } => { + ("profile-changed", Ok(serde_json::json!(current_profile_id))) + } + FrontendEvent::TimerUpdated { profile_index } => { + ("verge://timer-updated", Ok(serde_json::json!(profile_index))) + } + FrontendEvent::StartupCompleted => { + ("verge://startup-completed", Ok(serde_json::json!(null))) + } + FrontendEvent::ProfileUpdateStarted { uid } => { + ("profile-update-started", Ok(serde_json::json!({ "uid": uid }))) + } + FrontendEvent::ProfileUpdateCompleted { uid } => { + ("profile-update-completed", Ok(serde_json::json!({ "uid": uid }))) + } + }; + + if let Ok(payload) = payload_result { + match window.emit(event_name_str, payload) { + Ok(_) => { + system.stats.total_sent.fetch_add(1, Ordering::SeqCst); + // 记录成功发送的事件 + if log::log_enabled!(log::Level::Debug) { + log::debug!("Successfully emitted event: {event_name_str}"); + } + } + Err(e) => { + log::warn!("Failed to emit event {event_name_str}: {e}"); + system.stats.total_errors.fetch_add(1, Ordering::SeqCst); + *system.stats.last_error_time.write() = Some(Instant::now()); + + let errors = system.stats.total_errors.load(Ordering::SeqCst); + const EMIT_ERROR_THRESHOLD: u64 = 10; + if errors > EMIT_ERROR_THRESHOLD && !*system.emergency_mode.read() { + log::warn!( + "Reached {EMIT_ERROR_THRESHOLD} emit errors, entering emergency mode" + ); + *system.emergency_mode.write() = true; } } - } else { - system.stats.total_errors.fetch_add(1, Ordering::SeqCst); - *system.stats.last_error_time.write() = Some(Instant::now()); - log::warn!("Skipped emitting event due to payload serialization error for {event_name_str}"); } } else { - log::warn!("No window found, skipping event emit."); + system.stats.total_errors.fetch_add(1, Ordering::SeqCst); + *system.stats.last_error_time.write() = Some(Instant::now()); + log::warn!("Skipped emitting event due to payload serialization error for {event_name_str}"); } - thread::sleep(Duration::from_millis(20)); - } - Err(mpsc::RecvTimeoutError::Timeout) => { - continue; - } - Err(mpsc::RecvTimeoutError::Disconnected) => { - log::info!( - "Notification channel disconnected, exiting worker thread" - ); - break; + } else { + log::warn!("No window found, skipping event emit."); } + thread::sleep(Duration::from_millis(20)); + } + Err(mpsc::RecvTimeoutError::Timeout) => { + } + Err(mpsc::RecvTimeoutError::Disconnected) => { + log::info!( + "Notification channel disconnected, exiting worker thread" + ); + break; } } + } - log::info!("Notification worker thread exiting"); - }) - .expect("Failed to start notification worker thread"), - ); + log::info!("Notification worker thread exiting"); + }) { + Ok(handle) => { + self.worker_handle = Some(handle); + } + Err(e) => { + log::error!("Failed to start notification worker thread: {e}"); + } + } } /// 发送事件到队列 diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index 0a6ba7a3..89ba08be 100755 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -195,7 +195,9 @@ impl Hotkey { hotkey: &str, function: HotkeyFunction, ) -> Result<()> { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = handle::Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?; let manager = app_handle.global_shortcut(); logging!( @@ -351,7 +353,9 @@ impl Hotkey { } pub fn reset(&self) -> Result<()> { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = handle::Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?; let manager = app_handle.global_shortcut(); manager.unregister_all()?; Ok(()) @@ -364,7 +368,9 @@ impl Hotkey { } pub fn unregister(&self, hotkey: &str) -> Result<()> { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = handle::Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?; let manager = app_handle.global_shortcut(); manager.unregister(hotkey)?; logging!(debug, Type::Hotkey, "Unregister hotkey {}", hotkey); @@ -438,7 +444,17 @@ impl Hotkey { impl Drop for Hotkey { fn drop(&mut self) { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = match handle::Handle::global().app_handle() { + Some(handle) => handle, + None => { + logging!( + error, + Type::Hotkey, + "Failed to get app handle during hotkey cleanup" + ); + return; + } + }; if let Err(e) = app_handle.global_shortcut().unregister_all() { logging!( error, diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 3ec24829..cb684702 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -112,7 +112,7 @@ pub struct JsonResponse { } #[cfg(target_os = "windows")] -pub async fn uninstall_service() -> Result<()> { +pub fn uninstall_service() -> Result<()> { logging!(info, Type::Service, true, "uninstall service"); use deelevate::{PrivilegeLevel, Token}; @@ -138,7 +138,7 @@ pub async fn uninstall_service() -> Result<()> { if !status.success() { bail!( "failed to uninstall service with status {}", - status.code().unwrap() + status.code().unwrap_or(-1) ); } @@ -146,7 +146,7 @@ pub async fn uninstall_service() -> Result<()> { } #[cfg(target_os = "windows")] -pub async fn install_service() -> Result<()> { +pub fn install_service() -> Result<()> { logging!(info, Type::Service, true, "install service"); use deelevate::{PrivilegeLevel, Token}; @@ -172,7 +172,7 @@ pub async fn install_service() -> Result<()> { if !status.success() { bail!( "failed to install service with status {}", - status.code().unwrap() + status.code().unwrap_or(-1) ); } @@ -180,7 +180,7 @@ pub async fn install_service() -> Result<()> { } #[cfg(target_os = "windows")] -pub async fn reinstall_service() -> Result<()> { +pub fn reinstall_service() -> Result<()> { logging!(info, Type::Service, true, "reinstall service"); // 获取当前服务状态 @@ -198,7 +198,7 @@ pub async fn reinstall_service() -> Result<()> { } // 先卸载服务 - if let Err(err) = uninstall_service().await { + if let Err(err) = uninstall_service() { logging!( warn, Type::Service, @@ -209,7 +209,7 @@ pub async fn reinstall_service() -> Result<()> { } // 再安装服务 - match install_service().await { + match install_service() { Ok(_) => { // 记录安装信息并保存 service_state.record_install(); @@ -228,7 +228,7 @@ pub async fn reinstall_service() -> Result<()> { } #[cfg(target_os = "linux")] -pub async fn uninstall_service() -> Result<()> { +pub fn uninstall_service() -> Result<()> { logging!(info, Type::Service, true, "uninstall service"); use users::get_effective_uid; @@ -254,13 +254,13 @@ pub async fn uninstall_service() -> Result<()> { Type::Service, true, "uninstall status code:{}", - status.code().unwrap() + status.code().unwrap_or(-1) ); if !status.success() { bail!( "failed to uninstall service with status {}", - status.code().unwrap() + status.code().unwrap_or(-1) ); } @@ -268,7 +268,7 @@ pub async fn uninstall_service() -> Result<()> { } #[cfg(target_os = "linux")] -pub async fn install_service() -> Result<()> { +pub fn install_service() -> Result<()> { logging!(info, Type::Service, true, "install service"); use users::get_effective_uid; @@ -294,13 +294,13 @@ pub async fn install_service() -> Result<()> { Type::Service, true, "install status code:{}", - status.code().unwrap() + status.code().unwrap_or(-1) ); if !status.success() { bail!( "failed to install service with status {}", - status.code().unwrap() + status.code().unwrap_or(-1) ); } @@ -308,7 +308,7 @@ pub async fn install_service() -> Result<()> { } #[cfg(target_os = "linux")] -pub async fn reinstall_service() -> Result<()> { +pub fn reinstall_service() -> Result<()> { logging!(info, Type::Service, true, "reinstall service"); // 获取当前服务状态 @@ -326,7 +326,7 @@ pub async fn reinstall_service() -> Result<()> { } // 先卸载服务 - if let Err(err) = uninstall_service().await { + if let Err(err) = uninstall_service() { logging!( warn, Type::Service, @@ -337,7 +337,7 @@ pub async fn reinstall_service() -> Result<()> { } // 再安装服务 - match install_service().await { + match install_service() { Ok(_) => { // 记录安装信息并保存 service_state.record_install(); @@ -356,7 +356,7 @@ pub async fn reinstall_service() -> Result<()> { } #[cfg(target_os = "macos")] -pub async fn uninstall_service() -> Result<()> { +pub fn uninstall_service() -> Result<()> { use crate::utils::i18n::t; logging!(info, Type::Service, true, "uninstall service"); @@ -384,7 +384,7 @@ pub async fn uninstall_service() -> Result<()> { if !status.success() { bail!( "failed to uninstall service with status {}", - status.code().unwrap() + status.code().unwrap_or(-1) ); } @@ -392,7 +392,7 @@ pub async fn uninstall_service() -> Result<()> { } #[cfg(target_os = "macos")] -pub async fn install_service() -> Result<()> { +pub fn install_service() -> Result<()> { use crate::utils::i18n::t; logging!(info, Type::Service, true, "install service"); @@ -420,7 +420,7 @@ pub async fn install_service() -> Result<()> { if !status.success() { bail!( "failed to install service with status {}", - status.code().unwrap() + status.code().unwrap_or(-1) ); } @@ -428,7 +428,7 @@ pub async fn install_service() -> Result<()> { } #[cfg(target_os = "macos")] -pub async fn reinstall_service() -> Result<()> { +pub fn reinstall_service() -> Result<()> { logging!(info, Type::Service, true, "reinstall service"); // 获取当前服务状态 @@ -446,7 +446,7 @@ pub async fn reinstall_service() -> Result<()> { } // 先卸载服务 - if let Err(err) = uninstall_service().await { + if let Err(err) = uninstall_service() { logging!( warn, Type::Service, @@ -457,7 +457,7 @@ pub async fn reinstall_service() -> Result<()> { } // 再安装服务 - match install_service().await { + match install_service() { Ok(_) => { // 记录安装信息并保存 service_state.record_install(); @@ -856,17 +856,14 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> { if let Ok(()) = start_with_existing_service(config_file).await { log::info!(target: "app", "尽管版本不匹配,但成功启动了服务"); return Ok(()); - } else { - bail!("服务版本不匹配且无法重装,启动失败"); } + bail!("服务版本不匹配且无法重装,启动失败"); } log::info!(target: "app", "开始重装服务"); - if let Err(err) = reinstall_service().await { + if let Err(err) = reinstall_service() { log::warn!(target: "app", "服务重装失败: {err}"); - - log::info!(target: "app", "尝试使用现有服务"); - return start_with_existing_service(config_file).await; + bail!("Failed to reinstall service: {}", err); } log::info!(target: "app", "服务重装成功,尝试启动"); @@ -890,7 +887,7 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> { if check_service_needs_reinstall().await { log::info!(target: "app", "服务需要重装"); - if let Err(err) = reinstall_service().await { + if let Err(err) = reinstall_service() { log::warn!(target: "app", "服务重装失败: {err}"); bail!("Failed to reinstall service: {}", err); } @@ -970,7 +967,7 @@ pub async fn is_service_available() -> Result<()> { } /// 强制重装服务(UI修复按钮) -pub async fn force_reinstall_service() -> Result<()> { +pub fn force_reinstall_service() -> Result<()> { log::info!(target: "app", "用户请求强制重装服务"); let service_state = ServiceState::default(); @@ -978,7 +975,7 @@ pub async fn force_reinstall_service() -> Result<()> { log::info!(target: "app", "已重置服务状态,开始执行重装"); - match reinstall_service().await { + match reinstall_service() { Ok(()) => { log::info!(target: "app", "服务重装成功"); Ok(()) diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index 5dec7b6f..688995ee 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -149,7 +149,9 @@ impl Sysopt { use anyhow::bail; use tauri_plugin_shell::ShellExt; - let app_handle = Handle::global().app_handle().unwrap(); + let app_handle = Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("App handle not available"))?; let binary_path = dirs::service_path()?; let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); @@ -160,23 +162,27 @@ impl Sysopt { let shell = app_handle.shell(); let output = if pac_enable { let address = format!("http://{proxy_host}:{pac_port}/commands/pac"); - let output = shell - .command(sysproxy_exe.as_path().to_str().unwrap()) + let sysproxy_str = sysproxy_exe + .as_path() + .to_str() + .ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?; + shell + .command(sysproxy_str) .args(["pac", address.as_str()]) .output() - .await - .unwrap(); - output + .await? } else { let address = format!("{proxy_host}:{port}"); let bypass = get_bypass(); - let output = shell - .command(sysproxy_exe.as_path().to_str().unwrap()) + let sysproxy_str = sysproxy_exe + .as_path() + .to_str() + .ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?; + shell + .command(sysproxy_str) .args(["global", address.as_str(), bypass.as_ref()]) .output() - .await - .unwrap(); - output + .await? }; if !output.status.success() { @@ -218,7 +224,9 @@ impl Sysopt { use anyhow::bail; use tauri_plugin_shell::ShellExt; - let app_handle = Handle::global().app_handle().unwrap(); + let app_handle = Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("App handle not available"))?; let binary_path = dirs::service_path()?; let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); @@ -228,12 +236,15 @@ impl Sysopt { } let shell = app_handle.shell(); + let sysproxy_str = sysproxy_exe + .as_path() + .to_str() + .ok_or_else(|| anyhow::anyhow!("Invalid sysproxy.exe path"))?; let output = shell - .command(sysproxy_exe.as_path().to_str().unwrap()) + .command(sysproxy_str) .args(["set", "1"]) .output() - .await - .unwrap(); + .await?; if !output.status.success() { bail!("sysproxy exe run failed"); @@ -279,7 +290,10 @@ impl Sysopt { /// 尝试使用原来的自启动方法 fn try_original_autostart_method(&self, is_enable: bool) { - let app_handle = Handle::global().app_handle().unwrap(); + let Some(app_handle) = Handle::global().app_handle() else { + log::error!(target: "app", "App handle not available for autostart"); + return; + }; let autostart_manager = app_handle.autolaunch(); if is_enable { @@ -306,7 +320,9 @@ impl Sysopt { } // 回退到原来的方法 - let app_handle = Handle::global().app_handle().unwrap(); + let app_handle = Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("App handle not available"))?; let autostart_manager = app_handle.autolaunch(); match autostart_manager.is_enabled() { diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index 2cddf579..74ebf61b 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -71,9 +71,10 @@ impl TrayState { let verge = Config::verge().latest_ref().clone(); let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false); if is_common_tray_icon { - if let Some(common_icon_path) = find_target_icons("common").unwrap() { - let icon_data = fs::read(common_icon_path).unwrap(); - return (true, icon_data); + if let Ok(Some(common_icon_path)) = find_target_icons("common") { + if let Ok(icon_data) = fs::read(common_icon_path) { + return (true, icon_data); + } } } #[cfg(target_os = "macos")] @@ -105,9 +106,10 @@ impl TrayState { let verge = Config::verge().latest_ref().clone(); let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false); if is_sysproxy_tray_icon { - if let Some(sysproxy_icon_path) = find_target_icons("sysproxy").unwrap() { - let icon_data = fs::read(sysproxy_icon_path).unwrap(); - return (true, icon_data); + if let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy") { + if let Ok(icon_data) = fs::read(sysproxy_icon_path) { + return (true, icon_data); + } } } #[cfg(target_os = "macos")] @@ -139,9 +141,10 @@ impl TrayState { let verge = Config::verge().latest_ref().clone(); let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false); if is_tun_tray_icon { - if let Some(tun_icon_path) = find_target_icons("tun").unwrap() { - let icon_data = fs::read(tun_icon_path).unwrap(); - return (true, icon_data); + if let Ok(Some(tun_icon_path)) = find_target_icons("tun") { + if let Ok(icon_data) = fs::read(tun_icon_path) { + return (true, icon_data); + } } } #[cfg(target_os = "macos")] @@ -188,10 +191,14 @@ impl Tray { /// 更新托盘点击行为 pub fn update_click_behavior(&self) -> Result<()> { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = handle::Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?; let tray_event = { Config::verge().latest_ref().tray_event.clone() }; let tray_event: String = tray_event.unwrap_or("main_window".into()); - let tray = app_handle.tray_by_id("main").unwrap(); + let tray = app_handle + .tray_by_id("main") + .ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?; match tray_event.as_str() { "tray_menu" => tray.set_show_menu_on_left_click(true)?, _ => tray.set_show_menu_on_left_click(false)?, @@ -360,8 +367,12 @@ impl Tray { /// 更新托盘显示状态的函数 pub fn update_tray_display(&self) -> Result<()> { - let app_handle = handle::Handle::global().app_handle().unwrap(); - let _tray = app_handle.tray_by_id("main").unwrap(); + let app_handle = handle::Handle::global() + .app_handle() + .ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?; + let _tray = app_handle + .tray_by_id("main") + .ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?; // 更新菜单 self.update_menu()?; @@ -562,9 +573,8 @@ fn create_tray_menu( is_current_profile, None::<&str>, ) - .unwrap() }) - .collect(); + .collect::, _>>()?; let profile_menu_items: Vec<&dyn IsMenuItem> = profile_menu_items .iter() .map(|item| item as &dyn IsMenuItem) @@ -576,8 +586,7 @@ fn create_tray_menu( t("Dashboard"), true, hotkeys.get("open_or_close_dashboard").map(|s| s.as_str()), - ) - .unwrap(); + )?; let rule_mode = &CheckMenuItem::with_id( app_handle, @@ -586,8 +595,7 @@ fn create_tray_menu( true, mode == "rule", hotkeys.get("clash_mode_rule").map(|s| s.as_str()), - ) - .unwrap(); + )?; let global_mode = &CheckMenuItem::with_id( app_handle, @@ -596,8 +604,7 @@ fn create_tray_menu( true, mode == "global", hotkeys.get("clash_mode_global").map(|s| s.as_str()), - ) - .unwrap(); + )?; let direct_mode = &CheckMenuItem::with_id( app_handle, @@ -606,8 +613,7 @@ fn create_tray_menu( true, mode == "direct", hotkeys.get("clash_mode_direct").map(|s| s.as_str()), - ) - .unwrap(); + )?; let profiles = &Submenu::with_id_and_items( app_handle, @@ -615,8 +621,7 @@ fn create_tray_menu( t("Profiles"), true, &profile_menu_items, - ) - .unwrap(); + )?; let system_proxy = &CheckMenuItem::with_id( app_handle, @@ -625,8 +630,7 @@ fn create_tray_menu( true, system_proxy_enabled, hotkeys.get("toggle_system_proxy").map(|s| s.as_str()), - ) - .unwrap(); + )?; let tun_mode = &CheckMenuItem::with_id( app_handle, @@ -635,8 +639,7 @@ fn create_tray_menu( true, tun_mode_enabled, hotkeys.get("toggle_tun_mode").map(|s| s.as_str()), - ) - .unwrap(); + )?; let lighteweight_mode = &CheckMenuItem::with_id( app_handle, @@ -645,11 +648,9 @@ fn create_tray_menu( true, is_lightweight_mode, hotkeys.get("entry_lightweight_mode").map(|s| s.as_str()), - ) - .unwrap(); + )?; - let copy_env = - &MenuItem::with_id(app_handle, "copy_env", t("Copy Env"), true, None::<&str>).unwrap(); + let copy_env = &MenuItem::with_id(app_handle, "copy_env", t("Copy Env"), true, None::<&str>)?; let open_app_dir = &MenuItem::with_id( app_handle, @@ -657,8 +658,7 @@ fn create_tray_menu( t("Conf Dir"), true, None::<&str>, - ) - .unwrap(); + )?; let open_core_dir = &MenuItem::with_id( app_handle, @@ -666,8 +666,7 @@ fn create_tray_menu( t("Core Dir"), true, None::<&str>, - ) - .unwrap(); + )?; let open_logs_dir = &MenuItem::with_id( app_handle, @@ -675,8 +674,7 @@ fn create_tray_menu( t("Logs Dir"), true, None::<&str>, - ) - .unwrap(); + )?; let open_dir = &Submenu::with_id_and_items( app_handle, @@ -684,8 +682,7 @@ fn create_tray_menu( t("Open Dir"), true, &[open_app_dir, open_core_dir, open_logs_dir], - ) - .unwrap(); + )?; let restart_clash = &MenuItem::with_id( app_handle, @@ -693,8 +690,7 @@ fn create_tray_menu( t("Restart Clash Core"), true, None::<&str>, - ) - .unwrap(); + )?; let restart_app = &MenuItem::with_id( app_handle, @@ -702,8 +698,7 @@ fn create_tray_menu( t("Restart App"), true, None::<&str>, - ) - .unwrap(); + )?; let app_version = &MenuItem::with_id( app_handle, @@ -711,8 +706,7 @@ fn create_tray_menu( format!("{} {version}", t("Verge Version")), true, None::<&str>, - ) - .unwrap(); + )?; let more = &Submenu::with_id_and_items( app_handle, @@ -720,13 +714,11 @@ fn create_tray_menu( t("More"), true, &[restart_clash, restart_app, app_version], - ) - .unwrap(); + )?; - let quit = - &MenuItem::with_id(app_handle, "quit", t("Exit"), true, Some("CmdOrControl+Q")).unwrap(); + let quit = &MenuItem::with_id(app_handle, "quit", t("Exit"), true, Some("CmdOrControl+Q"))?; - let separator = &PredefinedMenuItem::separator(app_handle).unwrap(); + let separator = &PredefinedMenuItem::separator(app_handle)?; let menu = tauri::menu::MenuBuilder::new(app_handle) .items(&[ @@ -748,8 +740,7 @@ fn create_tray_menu( separator, quit, ]) - .build() - .unwrap(); + .build()?; Ok(menu) } diff --git a/src-tauri/src/core/win_uwp.rs b/src-tauri/src/core/win_uwp.rs index 4a19b93f..f76a6b30 100644 --- a/src-tauri/src/core/win_uwp.rs +++ b/src-tauri/src/core/win_uwp.rs @@ -6,7 +6,7 @@ use deelevate::{PrivilegeLevel, Token}; use runas::Command as RunasCommand; use std::process::Command as StdCommand; -pub async fn invoke_uwptools() -> Result<()> { +pub fn invoke_uwptools() -> Result<()> { let resource_dir = dirs::app_resources_dir()?; let tool_path = resource_dir.join("enableLoopback.exe"); diff --git a/src-tauri/src/enhance/merge.rs b/src-tauri/src/enhance/merge.rs index 0aa81e7e..1da09dec 100644 --- a/src-tauri/src/enhance/merge.rs +++ b/src-tauri/src/enhance/merge.rs @@ -18,7 +18,10 @@ pub fn use_merge(merge: Mapping, config: Mapping) -> Mapping { deep_merge(&mut config, &Value::from(merge)); - let config = config.as_mapping().unwrap().clone(); + let config = config.as_mapping().cloned().unwrap_or_else(|| { + log::error!("Failed to convert merged config to mapping, using empty mapping"); + Mapping::new() + }); config } diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs index 9ede0bad..20345833 100644 --- a/src-tauri/src/enhance/mod.rs +++ b/src-tauri/src/enhance/mod.rs @@ -14,7 +14,7 @@ type ResultLog = Vec<(String, String)>; /// Enhance mode /// 返回最终订阅、该订阅包含的键、和script执行的结果 -pub async fn enhance() -> (Mapping, Vec, HashMap) { +pub fn enhance() -> (Mapping, Vec, HashMap) { // config.yaml 的订阅 let clash_config = { Config::clash().latest_ref().0.clone() }; @@ -274,7 +274,7 @@ pub async fn enhance() -> (Mapping, Vec, HashMap) { }); } - config = use_tun(config, enable_tun).await; + config = use_tun(config, enable_tun); config = use_sort(config); // 应用独立的DNS配置(如果启用) diff --git a/src-tauri/src/enhance/script.rs b/src-tauri/src/enhance/script.rs index 11a89728..ae0e9e8b 100644 --- a/src-tauri/src/enhance/script.rs +++ b/src-tauri/src/enhance/script.rs @@ -7,23 +7,42 @@ pub fn use_script( config: Mapping, name: String, ) -> Result<(Mapping, Vec<(String, String)>)> { - use boa_engine::{native_function::NativeFunction, Context, JsValue, Source}; + use boa_engine::{native_function::NativeFunction, Context, JsString, JsValue, Source}; use std::{cell::RefCell, rc::Rc}; let mut context = Context::default(); let outputs = Rc::new(RefCell::new(vec![])); - let copy_outputs = outputs.clone(); + let copy_outputs = Rc::clone(&outputs); unsafe { let _ = context.register_global_builtin_callable( "__verge_log__".into(), 2, NativeFunction::from_closure( move |_: &JsValue, args: &[JsValue], context: &mut Context| { - let level = args.first().unwrap().to_string(context)?; - let level = level.to_std_string().unwrap(); - let data = args.get(1).unwrap().to_string(context)?; - let data = data.to_std_string().unwrap(); + let level = args.first().ok_or_else(|| { + boa_engine::JsError::from_opaque( + JsString::from("Missing level argument").into(), + ) + })?; + let level = level.to_string(context)?; + let level = level.to_std_string().map_err(|_| { + boa_engine::JsError::from_opaque( + JsString::from("Failed to convert level to string").into(), + ) + })?; + + let data = args.get(1).ok_or_else(|| { + boa_engine::JsError::from_opaque( + JsString::from("Missing data argument").into(), + ) + })?; + let data = data.to_string(context)?; + let data = data.to_std_string().map_err(|_| { + boa_engine::JsError::from_opaque( + JsString::from("Failed to convert data to string").into(), + ) + })?; let mut out = copy_outputs.borrow_mut(); out.push((level, data)); Ok(JsValue::undefined()) @@ -49,20 +68,24 @@ pub fn use_script( let safe_name = escape_js_string_for_single_quote(&name); let code = format!( - r#"try{{ + r"try{{ {script}; JSON.stringify(main({config_str},'{safe_name}')||'') }} catch(err) {{ `__error_flag__ ${{err.toString()}}` - }}"# + }}" ); if let Ok(result) = context.eval(Source::from_bytes(code.as_str())) { if !result.is_string() { anyhow::bail!("main function should return object"); } - let result = result.to_string(&mut context).unwrap(); - let result = result.to_std_string().unwrap(); + let result = result + .to_string(&mut context) + .map_err(|e| anyhow::anyhow!("Failed to convert JS result to string: {}", e))?; + let result = result + .to_std_string() + .map_err(|_| anyhow::anyhow!("Failed to convert JS string to std string"))?; // 直接解析JSON结果,不做其他解析 let res: Result = parse_json_safely(&result); @@ -102,6 +125,8 @@ fn escape_js_string_for_single_quote(s: &str) -> String { } #[test] +#[allow(unused_variables)] +#[allow(clippy::expect_used)] fn test_script() { let script = r#" function main(config) { @@ -114,7 +139,7 @@ fn test_script() { } "#; - let config = r#" + let config = r" rules: - 111 - 222 @@ -122,22 +147,21 @@ fn test_script() { enable: false dns: enable: false - "#; + "; - let config = serde_yaml::from_str(config).unwrap(); - let (config, results) = use_script(script.into(), config, "".to_string()).unwrap(); + let config = serde_yaml::from_str(config).expect("Failed to parse test config YAML"); + let (config, results) = use_script(script.into(), config, "".to_string()) + .expect("Script execution should succeed in test"); - let _ = serde_yaml::to_string(&config).unwrap(); + let _ = serde_yaml::to_string(&config).expect("Failed to serialize config to YAML"); let yaml_config_size = std::mem::size_of_val(&config); - dbg!(yaml_config_size); let box_yaml_config_size = std::mem::size_of_val(&Box::new(config)); - dbg!(box_yaml_config_size); - dbg!(results); assert!(box_yaml_config_size < yaml_config_size); } // 测试特殊字符转义功能 #[test] +#[allow(clippy::expect_used)] fn test_escape_unescape() { let test_string = r#"Hello "World"!\nThis is a test with \u00A9 copyright symbol."#; let escaped = escape_js_string_for_single_quote(test_string); @@ -145,13 +169,14 @@ fn test_escape_unescape() { println!("Escaped: {escaped}"); let json_str = r#"{"key":"value","nested":{"key":"value"}}"#; - let parsed = parse_json_safely(json_str).unwrap(); + let parsed = parse_json_safely(json_str).expect("Failed to parse test JSON safely"); assert!(parsed.contains_key("key")); assert!(parsed.contains_key("nested")); let quoted_json_str = r#""{"key":"value","nested":{"key":"value"}}""#; - let parsed_quoted = parse_json_safely(quoted_json_str).unwrap(); + let parsed_quoted = + parse_json_safely(quoted_json_str).expect("Failed to parse quoted test JSON safely"); assert!(parsed_quoted.contains_key("key")); assert!(parsed_quoted.contains_key("nested")); diff --git a/src-tauri/src/enhance/seq.rs b/src-tauri/src/enhance/seq.rs index 950a30b6..406adfe6 100644 --- a/src-tauri/src/enhance/seq.rs +++ b/src-tauri/src/enhance/seq.rs @@ -89,6 +89,8 @@ mod tests { use serde_yaml::Value; #[test] + #[allow(clippy::unwrap_used)] + #[allow(clippy::expect_used)] fn test_delete_proxy_and_references() { let config_str = r#" proxies: @@ -107,7 +109,8 @@ proxy-groups: proxies: - "proxy1" "#; - let mut config: Mapping = serde_yaml::from_str(config_str).unwrap(); + let mut config: Mapping = + serde_yaml::from_str(config_str).expect("Failed to parse test config YAML"); let seq = SeqMap { prepend: Sequence::new(), @@ -118,38 +121,51 @@ proxy-groups: config = use_seq(seq, config, "proxies"); // Check if proxy1 is removed from proxies - let proxies = config.get("proxies").unwrap().as_sequence().unwrap(); + let proxies = config + .get("proxies") + .expect("proxies field should exist") + .as_sequence() + .expect("proxies should be a sequence"); assert_eq!(proxies.len(), 1); assert_eq!( proxies[0] .as_mapping() - .unwrap() + .expect("proxy should be a mapping") .get("name") - .unwrap() + .expect("proxy should have name") .as_str() - .unwrap(), + .expect("name should be string"), "proxy2" ); // Check if proxy1 is removed from all groups - let groups = config.get("proxy-groups").unwrap().as_sequence().unwrap(); + let groups = config + .get("proxy-groups") + .expect("proxy-groups field should exist") + .as_sequence() + .expect("proxy-groups should be a sequence"); let group1_proxies = groups[0] .as_mapping() - .unwrap() + .expect("group should be a mapping") .get("proxies") - .unwrap() + .expect("group should have proxies") .as_sequence() - .unwrap(); + .expect("group proxies should be a sequence"); let group2_proxies = groups[1] .as_mapping() - .unwrap() + .expect("group should be a mapping") .get("proxies") - .unwrap() + .expect("group should have proxies") .as_sequence() - .unwrap(); + .expect("group proxies should be a sequence"); assert_eq!(group1_proxies.len(), 1); - assert_eq!(group1_proxies[0].as_str().unwrap(), "proxy2"); + assert_eq!( + group1_proxies[0] + .as_str() + .expect("proxy name should be string"), + "proxy2" + ); assert_eq!(group2_proxies.len(), 0); } } diff --git a/src-tauri/src/enhance/tun.rs b/src-tauri/src/enhance/tun.rs index 3e06b0c7..684f5d3c 100644 --- a/src-tauri/src/enhance/tun.rs +++ b/src-tauri/src/enhance/tun.rs @@ -18,7 +18,7 @@ macro_rules! append { }; } -pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping { +pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping { let tun_key = Value::from("tun"); let tun_val = config.get(&tun_key); let mut tun_val = tun_val.map_or(Mapping::new(), |val| { @@ -59,8 +59,10 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping { #[cfg(target_os = "macos")] { - crate::utils::resolve::restore_public_dns().await; - crate::utils::resolve::set_public_dns("223.6.6.6".to_string()).await; + tokio::spawn(async { + crate::utils::resolve::restore_public_dns().await; + crate::utils::resolve::set_public_dns("223.6.6.6".to_string()).await; + }); } } @@ -69,7 +71,9 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping { } else { // TUN未启用时,仅恢复系统DNS,不修改配置文件中的DNS设置 #[cfg(target_os = "macos")] - crate::utils::resolve::restore_public_dns().await; + tokio::spawn(async { + crate::utils::resolve::restore_public_dns().await; + }); } // 更新TUN配置 diff --git a/src-tauri/src/feat/backup.rs b/src-tauri/src/feat/backup.rs index 07adc5fc..c4fd656b 100644 --- a/src-tauri/src/feat/backup.rs +++ b/src-tauri/src/feat/backup.rs @@ -57,7 +57,9 @@ pub async fn restore_webdav_backup(filename: String) -> Result<()> { let webdav_username = verge_data.webdav_username.clone(); let webdav_password = verge_data.webdav_password.clone(); - let backup_storage_path = app_home_dir().unwrap().join(&filename); + let backup_storage_path = app_home_dir() + .map_err(|e| anyhow::anyhow!("Failed to get app home dir: {e}"))? + .join(&filename); backup::WebDavClient::global() .download(filename, backup_storage_path.clone()) .await diff --git a/src-tauri/src/feat/clash.rs b/src-tauri/src/feat/clash.rs index 33c1fb15..67a7d94b 100644 --- a/src-tauri/src/feat/clash.rs +++ b/src-tauri/src/feat/clash.rs @@ -2,7 +2,7 @@ use crate::{ config::Config, core::{handle, tray, CoreManager}, ipc::IpcManager, - logging_error, + logging, logging_error, process::AsyncHandler, utils::{logging::Type, resolve}, }; @@ -30,8 +30,11 @@ pub fn restart_app() { AsyncHandler::spawn(move || async move { logging_error!(Type::Core, true, CoreManager::global().stop_core().await); resolve::resolve_reset_async().await; - let app_handle = handle::Handle::global().app_handle().unwrap(); - std::thread::sleep(std::time::Duration::from_secs(1)); + let Some(app_handle) = handle::Handle::global().app_handle() else { + logging!(error, Type::Core, "Failed to get app handle for restart"); + return; + }; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; tauri::process::restart(&app_handle.env()); }); } diff --git a/src-tauri/src/feat/config.rs b/src-tauri/src/feat/config.rs index 7a7b7273..7a602ec2 100644 --- a/src-tauri/src/feat/config.rs +++ b/src-tauri/src/feat/config.rs @@ -15,7 +15,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { let res = { // 激活订阅 if patch.get("secret").is_some() || patch.get("external-controller").is_some() { - Config::generate().await?; + Config::generate()?; CoreManager::global().restart_core().await?; } else { if patch.get("mode").is_some() { @@ -173,7 +173,7 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { // Process updates based on flags if (update_flags & (UpdateFlags::RestartCore as i32)) != 0 { - Config::generate().await?; + Config::generate()?; CoreManager::global().restart_core().await?; } if (update_flags & (UpdateFlags::ClashConfig as i32)) != 0 { @@ -191,7 +191,9 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { sysopt::Sysopt::global().update_sysproxy().await?; } if (update_flags & (UpdateFlags::Hotkey as i32)) != 0 { - hotkey::Hotkey::global().update(patch.hotkeys.unwrap())?; + if let Some(hotkeys) = patch.hotkeys { + hotkey::Hotkey::global().update(hotkeys)?; + } } if (update_flags & (UpdateFlags::SystrayMenu as i32)) != 0 { tray::Tray::global().update_menu()?; @@ -206,7 +208,7 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> { tray::Tray::global().update_click_behavior()?; } if (update_flags & (UpdateFlags::LighteWeight as i32)) != 0 { - if enable_auto_light_weight.unwrap() { + if enable_auto_light_weight.unwrap_or(false) { lightweight::enable_auto_light_weight_mode(); } else { lightweight::disable_auto_light_weight_mode(); diff --git a/src-tauri/src/feat/profile.rs b/src-tauri/src/feat/profile.rs index 281fa7ed..893aa7e8 100644 --- a/src-tauri/src/feat/profile.rs +++ b/src-tauri/src/feat/profile.rs @@ -11,7 +11,14 @@ use anyhow::{bail, Result}; /// Toggle proxy profile pub fn toggle_proxy_profile(profile_index: String) { AsyncHandler::spawn(|| async move { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let Some(app_handle) = handle::Handle::global().app_handle() else { + logging!( + error, + Type::Config, + "Failed to get app handle for profile toggle" + ); + return; + }; match cmd::patch_profiles_config_by_profile_index(app_handle, profile_index).await { Ok(_) => { let _ = tray::Tray::global().update_menu(); @@ -50,9 +57,14 @@ pub async fn update_profile( log::info!(target: "app", "[订阅更新] {} 是远程订阅,URL: {}", uid, - item.url.clone().unwrap() + item.url.clone().ok_or_else(|| anyhow::anyhow!("Profile URL is None"))? ); - Some((item.url.clone().unwrap(), item.option.clone())) + Some(( + item.url + .clone() + .ok_or_else(|| anyhow::anyhow!("Profile URL is None"))?, + item.option.clone(), + )) } }; @@ -137,7 +149,13 @@ pub async fn update_profile( logging!(info, Type::Config, true, "[订阅更新] 更新成功"); handle::Handle::refresh_clash(); if let Err(err) = cmd::proxy::force_refresh_proxies().await { - logging!(error, Type::Config, true, "[订阅更新] 代理组刷新失败: {}", err); + logging!( + error, + Type::Config, + true, + "[订阅更新] 代理组刷新失败: {}", + err + ); } } Err(err) => { diff --git a/src-tauri/src/feat/proxy.rs b/src-tauri/src/feat/proxy.rs index c3564238..bbe31583 100644 --- a/src-tauri/src/feat/proxy.rs +++ b/src-tauri/src/feat/proxy.rs @@ -2,7 +2,9 @@ use crate::{ config::{Config, IVerge}, core::handle, ipc::IpcManager, + logging, process::AsyncHandler, + utils::logging::Type, }; use std::env; use tauri_plugin_clipboard_manager::ClipboardExt; @@ -71,7 +73,14 @@ pub fn copy_clash_env() { .unwrap_or_else(|| "127.0.0.1".to_string()) }); - let app_handle = handle::Handle::global().app_handle().unwrap(); + let Some(app_handle) = handle::Handle::global().app_handle() else { + logging!( + error, + Type::System, + "Failed to get app handle for proxy operation" + ); + return; + }; let port = { Config::verge() .latest_ref() diff --git a/src-tauri/src/feat/window.rs b/src-tauri/src/feat/window.rs index 951c2bf7..29f30713 100644 --- a/src-tauri/src/feat/window.rs +++ b/src-tauri/src/feat/window.rs @@ -67,7 +67,14 @@ pub fn quit() { logging!(debug, Type::System, true, "启动退出流程"); // 获取应用句柄并设置退出标志 - let app_handle = handle::Handle::global().app_handle().unwrap(); + let Some(app_handle) = handle::Handle::global().app_handle() else { + logging!( + error, + Type::System, + "Failed to get app handle for quit operation" + ); + return; + }; handle::Handle::global().set_is_exiting(); // 优先关闭窗口,提供立即反馈 diff --git a/src-tauri/src/ipc/general.rs b/src-tauri/src/ipc/general.rs index 19ee27ea..5818cb2c 100644 --- a/src-tauri/src/ipc/general.rs +++ b/src-tauri/src/ipc/general.rs @@ -26,7 +26,16 @@ pub struct IpcManager { impl IpcManager { fn new() -> Self { - let ipc_path_buf = ipc_path().unwrap(); + let ipc_path_buf = ipc_path().unwrap_or_else(|e| { + logging!( + error, + crate::utils::logging::Type::Ipc, + true, + "Failed to get IPC path: {}", + e + ); + std::path::PathBuf::from("/tmp/clash-verge-ipc") // fallback path + }); let ipc_path = ipc_path_buf.to_str().unwrap_or_default(); Self { ipc_path: ipc_path.to_string(), diff --git a/src-tauri/src/ipc/logs.rs b/src-tauri/src/ipc/logs.rs index 83248327..4307354f 100644 --- a/src-tauri/src/ipc/logs.rs +++ b/src-tauri/src/ipc/logs.rs @@ -28,7 +28,7 @@ impl LogItem { let now = SystemTime::now() .duration_since(UNIX_EPOCH) - .unwrap() + .unwrap_or_else(|_| std::time::Duration::from_secs(0)) .as_secs(); // Simple time formatting (HH:MM:SS) @@ -109,9 +109,8 @@ impl LogsMonitor { filter_level ); return; - } else { - true // Level changed } + true // Level changed } else { true // First time or was stopped } @@ -152,12 +151,12 @@ impl LogsMonitor { *current_level = Some(filter_level.clone()); } - let monitor_current = self.current.clone(); + let monitor_current = Arc::clone(&self.current); let task = tokio::spawn(async move { loop { // Get fresh IPC path and client for each connection attempt - let (_ipc_path_buf, client) = match Self::create_ipc_client().await { + let (_ipc_path_buf, client) = match Self::create_ipc_client() { Ok((path, client)) => (path, client), Err(e) => { logging!(error, Type::Ipc, true, "Failed to create IPC client: {}", e); @@ -183,7 +182,9 @@ impl LogsMonitor { let _ = client .get(&url) .timeout(Duration::from_secs(30)) - .process_lines(|line| Self::process_log_line(line, monitor_current.clone())) + .process_lines(|line| { + Self::process_log_line(line, Arc::clone(&monitor_current)) + }) .await; // Wait before retrying @@ -228,7 +229,7 @@ impl LogsMonitor { } } - async fn create_ipc_client() -> Result< + fn create_ipc_client() -> Result< (std::path::PathBuf, kode_bridge::IpcStreamClient), Box, > { diff --git a/src-tauri/src/ipc/monitor.rs b/src-tauri/src/ipc/monitor.rs index 261df1c9..b1573f37 100644 --- a/src-tauri/src/ipc/monitor.rs +++ b/src-tauri/src/ipc/monitor.rs @@ -51,7 +51,7 @@ where freshness_duration: Duration, ) -> Self { let current = Arc::new(RwLock::new(T::default())); - let monitor_current = current.clone(); + let monitor_current = Arc::clone(¤t); let endpoint_clone = endpoint.clone(); // Start the monitoring task @@ -110,7 +110,7 @@ where let _ = client .get(&endpoint) .timeout(timeout) - .process_lines(|line| T::parse_and_update(line, current.clone())) + .process_lines(|line| T::parse_and_update(line, Arc::clone(¤t))) .await; tokio::time::sleep(retry_interval).await; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 37c3628b..21b158b5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -60,7 +60,16 @@ impl AppHandleManager { /// Get the app handle, panics if it hasn't been initialized. pub fn get_handle(&self) -> AppHandle { - self.get().expect("AppHandle not initialized") + if let Some(handle) = self.get() { + handle + } else { + logging!( + error, + Type::Setup, + "AppHandle not initialized - ensure init() was called first" + ); + std::process::exit(1) + } } /// Check if the app handle has been initialized. @@ -181,9 +190,7 @@ mod app_init { builder = builder.plugin(tauri_plugin_devtools::init()); } - builder.manage(std::sync::Mutex::new( - state::lightweight::LightWeightState::default(), - )) + builder.manage(Mutex::new(state::lightweight::LightWeightState::default())) } /// Setup deep link handling @@ -667,7 +674,16 @@ pub fn run() { // Build the application let app = builder .build(tauri::generate_context!()) - .expect("error while running tauri application"); + .unwrap_or_else(|e| { + logging!( + error, + Type::Setup, + true, + "Failed to build Tauri application: {}", + e + ); + std::process::exit(1); + }); app.run(|app_handle, e| match e { tauri::RunEvent::Ready | tauri::RunEvent::Resumed => { diff --git a/src-tauri/src/module/lightweight.rs b/src-tauri/src/module/lightweight.rs index 98d5ba97..72a4a780 100644 --- a/src-tauri/src/module/lightweight.rs +++ b/src-tauri/src/module/lightweight.rs @@ -124,7 +124,10 @@ pub fn set_lightweight_mode(value: bool) { } pub fn enable_auto_light_weight_mode() { - Timer::global().init().unwrap(); + if let Err(e) = Timer::global().init() { + logging!(error, Type::Lightweight, "Failed to initialize timer: {e}"); + return; + } logging!(info, Type::Lightweight, true, "开启自动轻量模式"); setup_window_close_listener(); setup_webview_focus_listener(); diff --git a/src-tauri/src/module/sysinfo.rs b/src-tauri/src/module/sysinfo.rs index 0d594fb8..7e6a97ea 100644 --- a/src-tauri/src/module/sysinfo.rs +++ b/src-tauri/src/module/sysinfo.rs @@ -32,7 +32,17 @@ impl PlatformSpecification { let system_kernel_version = System::kernel_version().unwrap_or("Null".into()); let system_arch = System::cpu_arch(); - let handler = handle::Handle::global().app_handle().unwrap(); + let Some(handler) = handle::Handle::global().app_handle() else { + return Self { + system_name, + system_version, + system_kernel_version, + system_arch, + verge_version: "unknown".into(), + running_mode: "NotRunning".to_string(), + is_admin: false, + }; + }; let verge_version = handler.package_info().version.to_string(); // 使用默认值避免在同步上下文中执行异步操作 @@ -52,10 +62,10 @@ impl PlatformSpecification { } // 异步方法来获取完整的系统信息 - pub async fn new_async() -> Self { + pub fn new_sync() -> Self { let mut info = Self::new(); - let running_mode = CoreManager::global().get_running_mode().await; + let running_mode = CoreManager::global().get_running_mode(); info.running_mode = running_mode.to_string(); info diff --git a/src-tauri/src/state/proxy.rs b/src-tauri/src/state/proxy.rs index 777eacda..9d894197 100644 --- a/src-tauri/src/state/proxy.rs +++ b/src-tauri/src/state/proxy.rs @@ -49,7 +49,7 @@ impl ProxyRequestCache { self.map .remove_if(&key_cloned, |_, v| Arc::ptr_eq(v, &cell)); let new_cell = Arc::new(OnceCell::new()); - self.map.insert(key_cloned.clone(), new_cell.clone()); + self.map.insert(key_cloned.clone(), Arc::clone(&new_cell)); return Box::pin(self.get_or_fetch(key_cloned, ttl, fetch_fn)).await; } } @@ -60,7 +60,13 @@ impl ProxyRequestCache { expires_at: Instant::now() + ttl, }; let _ = cell.set(entry); - Arc::clone(&cell.get().unwrap().value) + // Safe to unwrap here as we just set the value + Arc::clone( + &cell + .get() + .unwrap_or_else(|| unreachable!("Cell value should exist after set")) + .value, + ) } } diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 8f58936a..225de7df 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -163,8 +163,13 @@ pub fn find_target_icons(target: &str) -> Result> { if matching_files.is_empty() { Ok(None) } else { - let first = path_to_str(matching_files.first().unwrap())?; - Ok(Some(first.to_string())) + match matching_files.first() { + Some(first_path) => { + let first = path_to_str(first_path)?; + Ok(Some(first.to_string())) + } + None => Ok(None), + } } } diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index cfa39da0..174c66c8 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -401,7 +401,14 @@ pub fn init_scheme() -> Result<()> { } pub async fn startup_script() -> Result<()> { - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = match handle::Handle::global().app_handle() { + Some(handle) => handle, + None => { + return Err(anyhow::anyhow!( + "app_handle not available for startup script execution" + )); + } + }; let script_path = { let verge = Config::verge(); diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index 6532a5d0..600ceade 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -40,13 +40,37 @@ singleton_lazy!(NetworkManager, NETWORK_MANAGER, NetworkManager::new); impl NetworkManager { fn new() -> Self { // 创建专用的异步运行时,线程数限制为4个 - let runtime = Builder::new_multi_thread() + let runtime = match Builder::new_multi_thread() .worker_threads(4) .thread_name("clash-verge-network") .enable_io() .enable_time() .build() - .expect("Failed to create network runtime"); + { + Ok(runtime) => runtime, + Err(e) => { + log::error!( + "Failed to create network runtime: {}. Using fallback single-threaded runtime.", + e + ); + // Fallback to current thread runtime + match Builder::new_current_thread() + .enable_io() + .enable_time() + .thread_name("clash-verge-network-fallback") + .build() + { + Ok(fallback_runtime) => fallback_runtime, + Err(fallback_err) => { + log::error!( + "Failed to create fallback runtime: {}. This is critical.", + fallback_err + ); + std::process::exit(1); + } + } + } + }; NetworkManager { runtime: Arc::new(runtime), @@ -66,7 +90,7 @@ impl NetworkManager { logging!(info, Type::Network, true, "初始化网络管理器"); // 创建无代理客户端 - let no_proxy_client = ClientBuilder::new() + let no_proxy_client = match ClientBuilder::new() .use_rustls_tls() .no_proxy() .pool_max_idle_per_host(POOL_MAX_IDLE_PER_HOST) @@ -74,7 +98,19 @@ impl NetworkManager { .connect_timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(30)) .build() - .expect("Failed to build no_proxy client"); + { + Ok(client) => client, + Err(e) => { + logging!( + error, + Type::Network, + true, + "Failed to build no_proxy client: {}", + e + ); + return; + } + }; let mut no_proxy_guard = NetworkManager::global().no_proxy_client.lock(); *no_proxy_guard = Some(no_proxy_client); @@ -214,7 +250,45 @@ impl NetworkManager { builder = builder.user_agent(version); } - let client = builder.build().expect("Failed to build custom HTTP client"); + let client = match builder.build() { + Ok(client) => client, + Err(e) => { + logging!( + error, + Type::Network, + true, + "Failed to build custom HTTP client: {}", + e + ); + // Return a simple no-proxy client as fallback + match ClientBuilder::new() + .use_rustls_tls() + .no_proxy() + .timeout(DEFAULT_REQUEST_TIMEOUT) + .build() + { + Ok(fallback_client) => fallback_client, + Err(fallback_err) => { + logging!( + error, + Type::Network, + true, + "Failed to create fallback client: {}", + fallback_err + ); + self.record_connection_error(&format!( + "Critical client build failure: {}", + fallback_err + )); + // Return a minimal client that will likely fail but won't panic + ClientBuilder::new().build().unwrap_or_else(|_| { + // If even the most basic client fails, this is truly critical + std::process::exit(1); + }) + } + } + } + }; client.get(url) } diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index c05645db..0aa68166 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -344,8 +344,21 @@ pub fn create_window(is_show: bool) -> bool { logging!(debug, Type::Window, true, "[ScopeGuard] 窗口创建状态已重置"); }); + let app_handle = match handle::Handle::global().app_handle() { + Some(handle) => handle, + None => { + logging!( + error, + Type::Window, + true, + "无法获取app_handle,窗口创建失败" + ); + return false; + } + }; + match tauri::WebviewWindowBuilder::new( - &handle::Handle::global().app_handle().unwrap(), + &app_handle, "main", /* the unique window label */ tauri::WebviewUrl::App("index.html".into()), ) @@ -544,13 +557,13 @@ pub fn create_window(is_show: bool) -> bool { logging!(info, Type::Window, true, "UI已完全加载就绪"); handle::Handle::global() .get_window() - .map(|window| window.eval(r#" + .map(|window| window.eval(r" const overlay = document.getElementById('initial-loading-overlay'); if (overlay) { overlay.style.opacity = '0'; setTimeout(() => overlay.remove(), 300); } - "#)); + ")); } Err(_) => { logging!( @@ -647,7 +660,17 @@ pub async fn resolve_scheme(param: String) -> Result<()> { create_window(false); match PrfItem::from_url(url.as_ref(), name, None, None).await { Ok(item) => { - let uid = item.uid.clone().unwrap(); + let uid = match item.uid.clone() { + Some(uid) => uid, + None => { + logging!(error, Type::Config, true, "Profile item missing UID"); + handle::Handle::notice_message( + "import_sub_url::error", + "Profile item missing UID".to_string(), + ); + return Ok(()); + } + }; let _ = wrap_err!(Config::profiles().data_mut().append_item(item)); handle::Handle::notice_message("import_sub_url::ok", uid); } @@ -725,10 +748,22 @@ async fn resolve_random_port_config() -> Result<()> { pub async fn set_public_dns(dns_server: String) { use crate::{core::handle, utils::dirs}; use tauri_plugin_shell::ShellExt; - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = match handle::Handle::global().app_handle() { + Some(handle) => handle, + None => { + log::error!(target: "app", "app_handle not available for DNS configuration"); + return; + } + }; log::info!(target: "app", "try to set system dns"); - let resource_dir = dirs::app_resources_dir().unwrap(); + let resource_dir = match dirs::app_resources_dir() { + Ok(dir) => dir, + Err(e) => { + log::error!(target: "app", "Failed to get resource directory: {}", e); + return; + } + }; let script = resource_dir.join("set_dns.sh"); if !script.exists() { log::error!(target: "app", "set_dns.sh not found"); @@ -761,9 +796,21 @@ pub async fn set_public_dns(dns_server: String) { pub async fn restore_public_dns() { use crate::{core::handle, utils::dirs}; use tauri_plugin_shell::ShellExt; - let app_handle = handle::Handle::global().app_handle().unwrap(); + let app_handle = match handle::Handle::global().app_handle() { + Some(handle) => handle, + None => { + log::error!(target: "app", "app_handle not available for DNS restoration"); + return; + } + }; log::info!(target: "app", "try to unset system dns"); - let resource_dir = dirs::app_resources_dir().unwrap(); + let resource_dir = match dirs::app_resources_dir() { + Ok(dir) => dir, + Err(e) => { + log::error!(target: "app", "Failed to get resource directory: {}", e); + return; + } + }; let script = resource_dir.join("unset_dns.sh"); if !script.exists() { log::error!(target: "app", "unset_dns.sh not found"); diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index 1cdd16cf..2e48d3a1 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -38,9 +38,8 @@ pub async fn check_singleton() -> Result<()> { } log::error!("failed to setup singleton listen server"); bail!("app exists"); - } else { - Ok(()) } + Ok(()) } /// The embed server only be used to implement singleton process