From 2ba5eaa4c835b6e4d4250d7971a8a4cac16de006 Mon Sep 17 00:00:00 2001 From: coso Date: Tue, 3 Feb 2026 03:27:40 +0800 Subject: [PATCH] =?UTF-8?q?release:=20v0.54.0=20-=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=B4=A8=E9=87=8F=E6=94=B9=E8=BF=9B=E4=B8=8E=20Clippy=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新版本号到 0.54.0 - 修复所有 ESLint 警告 - 通过 cargo clippy --fix 自动修复大量代码风格问题 - 修复 format! 字符串内联变量 - 修复 &PathBuf 改为 &Path - 使用 #[derive(Default)] 替代手动实现 - 修复 aster 依赖版本 (v0.7.1 -> v0.7.0) Co-Authored-By: Claude Opus 4.5 --- package.json | 3 +- src-tauri/Cargo.lock | 32 +-- src-tauri/Cargo.toml | 8 +- src-tauri/crates/core/src/logger.rs | 2 +- .../core/src/models/kiro_fingerprint.rs | 13 +- .../crates/core/src/models/model_registry.rs | 19 +- .../crates/core/src/models/route_model.rs | 12 +- .../crates/infra/src/resilience/failover.rs | 6 +- .../crates/infra/src/resilience/timeout.rs | 12 +- .../crates/infra/src/telemetry/logger.rs | 18 +- .../crates/infra/src/telemetry/tokens.rs | 2 +- .../voice-core/src/asr_client/openai.rs | 3 +- .../voice-core/src/asr_client/xunfei.rs | 10 +- .../proptest-regressions/config/tests.txt | 7 + src-tauri/src/agent/aster_agent.rs | 134 +++++++---- src-tauri/src/agent/aster_state.rs | 82 ++++--- src-tauri/src/agent/credential_bridge.rs | 42 ++-- src-tauri/src/agent/event_converter.rs | 6 +- src-tauri/src/app/bootstrap.rs | 35 ++- src-tauri/src/app/commands/config.rs | 14 +- src-tauri/src/app/runner.rs | 14 +- src-tauri/src/app/setup.rs | 6 +- src-tauri/src/backends/traits.rs | 4 +- src-tauri/src/browser_interceptor/config.rs | 27 +-- .../src/browser_interceptor/interceptor.rs | 13 +- src-tauri/src/browser_interceptor/mod.rs | 14 +- .../src/browser_interceptor/platform/macos.rs | 7 +- .../src/browser_interceptor/state_manager.rs | 25 +- .../src/browser_interceptor/url_manager.rs | 78 +++--- src-tauri/src/commands/agent_cmd.rs | 37 ++- .../src/commands/api_key_provider_cmd.rs | 8 +- src-tauri/src/commands/asr_cmd.rs | 6 +- src-tauri/src/commands/aster_agent_cmd.rs | 117 ++++----- src-tauri/src/commands/auto_fix_cmd.rs | 20 +- .../src/commands/browser_interceptor_cmd.rs | 4 +- src-tauri/src/commands/config_cmd.rs | 42 ++-- src-tauri/src/commands/connect_cmd.rs | 12 +- src-tauri/src/commands/connection_cmd.rs | 12 +- src-tauri/src/commands/flow_monitor_cmd.rs | 130 +++++----- src-tauri/src/commands/general_chat_cmd.rs | 79 ++++--- src-tauri/src/commands/injection_cmd.rs | 4 +- src-tauri/src/commands/kiro_local.rs | 55 ++--- src-tauri/src/commands/machine_id_cmd.rs | 12 +- src-tauri/src/commands/model_cmd.rs | 4 +- src-tauri/src/commands/model_registry_cmd.rs | 6 +- src-tauri/src/commands/models_cmd.rs | 14 +- src-tauri/src/commands/music_cmd.rs | 23 +- src-tauri/src/commands/network_cmd.rs | 4 +- src-tauri/src/commands/orchestrator_cmd.rs | 12 +- src-tauri/src/commands/plugin_cmd.rs | 12 +- src-tauri/src/commands/plugin_install_cmd.rs | 2 +- src-tauri/src/commands/plugin_rpc_cmd.rs | 28 +-- src-tauri/src/commands/provider_pool_cmd.rs | 183 +++++++------- src-tauri/src/commands/route_cmd.rs | 4 +- src-tauri/src/commands/screenshot_cmd.rs | 27 +-- src-tauri/src/commands/session_files_cmd.rs | 26 +- src-tauri/src/commands/skill_cmd.rs | 2 +- src-tauri/src/commands/switch_cmd.rs | 13 +- src-tauri/src/commands/telemetry_cmd.rs | 14 +- src-tauri/src/commands/tray_cmd.rs | 2 +- src-tauri/src/commands/unified_chat_cmd.rs | 65 +++-- src-tauri/src/commands/update_cmd.rs | 25 +- src-tauri/src/commands/usage_cmd.rs | 14 +- src-tauri/src/commands/webview_cmd.rs | 20 +- src-tauri/src/commands/workspace_cmd.rs | 2 +- src-tauri/src/config/export.rs | 14 +- src-tauri/src/config/hot_reload.rs | 10 +- src-tauri/src/config/import.rs | 20 +- src-tauri/src/config/tests.rs | 8 +- src-tauri/src/config/types.rs | 11 +- src-tauri/src/config/yaml.rs | 10 +- src-tauri/src/connect/deep_link.rs | 18 +- src-tauri/src/connect/registry.rs | 22 +- src-tauri/src/content/manager.rs | 75 ++---- .../src/converter/openai_to_antigravity.rs | 30 +-- src-tauri/src/converter/openai_to_cw.rs | 2 +- src-tauri/src/converter/reasoning_handler.rs | 9 +- src-tauri/src/credential/balancer.rs | 6 +- src-tauri/src/credential/health.rs | 2 +- src-tauri/src/credential/pool.rs | 6 +- src-tauri/src/credential/quota.rs | 8 +- src-tauri/src/credential/risk.rs | 2 +- src-tauri/src/credential/sync.rs | 16 +- src-tauri/src/credential/tests.rs | 76 +++--- src-tauri/src/database/dao/agent.rs | 2 +- .../src/database/dao/api_key_provider.rs | 4 +- src-tauri/src/database/dao/chat.rs | 9 +- src-tauri/src/database/dao/general_chat.rs | 16 +- .../src/database/dao/installed_plugins.rs | 18 +- src-tauri/src/database/dao/orchestrator.rs | 4 +- src-tauri/src/database/migration.rs | 48 ++-- src-tauri/src/database/mod.rs | 17 +- src-tauri/src/dev_bridge.rs | 4 +- src-tauri/src/dev_bridge/dispatcher.rs | 25 +- src-tauri/src/flow_monitor/batch_ops.rs | 18 +- src-tauri/src/flow_monitor/bookmark.rs | 4 +- src-tauri/src/flow_monitor/code_exporter.rs | 24 +- src-tauri/src/flow_monitor/diff.rs | 77 +++--- src-tauri/src/flow_monitor/enhanced_stats.rs | 33 +-- src-tauri/src/flow_monitor/exporter.rs | 52 ++-- src-tauri/src/flow_monitor/file_store.rs | 36 ++- src-tauri/src/flow_monitor/filter_parser.rs | 82 ++++--- src-tauri/src/flow_monitor/interceptor.rs | 32 +-- src-tauri/src/flow_monitor/memory_store.rs | 53 ++--- src-tauri/src/flow_monitor/models.rs | 34 +-- src-tauri/src/flow_monitor/monitor.rs | 69 ++---- src-tauri/src/flow_monitor/query_service.rs | 36 ++- src-tauri/src/flow_monitor/quick_filter.rs | 10 +- src-tauri/src/flow_monitor/replayer.rs | 4 +- src-tauri/src/flow_monitor/session.rs | 4 +- .../src/flow_monitor/stream_rebuilder.rs | 65 ++--- src-tauri/src/memory/manager.rs | 130 ++++------ src-tauri/src/memory/types.rs | 13 +- src-tauri/src/middleware/tests.rs | 12 +- src-tauri/src/models/kiro_fingerprint.rs | 13 +- src-tauri/src/models/model_registry.rs | 19 +- src-tauri/src/models/route_model.rs | 12 +- src-tauri/src/orchestrator/fallback.rs | 24 +- src-tauri/src/orchestrator/selector.rs | 4 +- .../orchestrator/strategies/cost_optimized.rs | 2 +- .../orchestrator/strategies/load_balanced.rs | 2 +- .../orchestrator/strategies/round_robin.rs | 2 +- .../src/orchestrator/strategies/task_based.rs | 2 +- src-tauri/src/orchestrator/tier.rs | 8 +- src-tauri/src/plugin/binary_downloader.rs | 48 ++-- src-tauri/src/plugin/installer/downloader.rs | 20 +- src-tauri/src/plugin/installer/installer.rs | 50 ++-- src-tauri/src/plugin/installer/registry.rs | 11 +- src-tauri/src/plugin/installer/tests.rs | 12 +- src-tauri/src/plugin/installer/validator.rs | 62 +++-- src-tauri/src/plugin/loader.rs | 6 +- src-tauri/src/plugin/manager.rs | 8 +- src-tauri/src/providers/antigravity.rs | 126 +++++----- src-tauri/src/providers/claude_custom.rs | 12 +- src-tauri/src/providers/claude_oauth.rs | 16 +- src-tauri/src/providers/codex.rs | 68 +++--- src-tauri/src/providers/error.rs | 22 +- src-tauri/src/providers/gemini.rs | 27 +-- src-tauri/src/providers/kiro.rs | 41 ++-- src-tauri/src/providers/openai_custom.rs | 18 +- src-tauri/src/providers/tests.rs | 56 ++--- src-tauri/src/providers/vertex.rs | 8 +- src-tauri/src/router/provider_router.rs | 4 +- src-tauri/src/router/route_registry.rs | 2 +- src-tauri/src/screenshot/capture.rs | 9 +- src-tauri/src/screenshot/config.rs | 2 +- src-tauri/src/screenshot/shortcut.rs | 22 +- src-tauri/src/screenshot/window.rs | 35 ++- src-tauri/src/server/handlers/api.rs | 200 +++++++--------- .../src/server/handlers/credentials_api.rs | 14 +- .../src/server/handlers/image_handler.rs | 12 +- .../src/server/handlers/kiro_credential.rs | 36 +-- src-tauri/src/server/handlers/management.rs | 4 +- .../src/server/handlers/provider_calls.rs | 92 ++++---- src-tauri/src/server/handlers/websocket.rs | 41 ++-- src-tauri/src/server/mod.rs | 77 +++--- src-tauri/src/server_utils.rs | 33 +-- .../src/services/api_key_provider_service.rs | 171 ++++++-------- src-tauri/src/services/aster_session_store.rs | 112 +++++---- src-tauri/src/services/backup_service.rs | 26 +- .../src/services/context_memory_service.rs | 20 +- .../src/services/file_browser_service.rs | 26 +- .../services/general_chat/session_service.rs | 2 +- src-tauri/src/services/live_sync.rs | 2 +- src-tauri/src/services/live_sync_tests.rs | 9 +- src-tauri/src/services/machine_id_service.rs | 36 +-- .../src/services/model_registry_service.rs | 35 +-- src-tauri/src/services/model_service.rs | 26 +- .../src/services/provider_pool_service.rs | 191 +++++++-------- .../src/services/session_context_service.rs | 10 +- src-tauri/src/services/skill_service.rs | 9 +- src-tauri/src/services/switch.rs | 4 +- src-tauri/src/services/sysinfo_service.rs | 2 +- src-tauri/src/services/token_cache_service.rs | 50 ++-- src-tauri/src/services/tool_hooks_service.rs | 20 +- .../src/services/update_check_service.rs | 18 +- src-tauri/src/services/update_window.rs | 12 +- src-tauri/src/services/usage_service.rs | 20 +- src-tauri/src/session/rate_limit.rs | 8 +- src-tauri/src/session_files/storage.rs | 42 ++-- .../src/stream/generators/anthropic_sse.rs | 29 +-- src-tauri/src/stream/generators/openai_sse.rs | 4 +- .../src/stream/parsers/aws_event_stream.rs | 2 +- src-tauri/src/streaming/anthropic_sse.rs | 16 +- src-tauri/src/streaming/aws_parser.rs | 18 +- src-tauri/src/streaming/converter.rs | 32 +-- src-tauri/src/streaming/error.rs | 14 +- src-tauri/src/streaming/manager.rs | 11 +- src-tauri/src/streaming/metrics.rs | 4 +- src-tauri/src/streaming/traits.rs | 2 +- .../block_controller/shell_controller.rs | 21 +- .../src/terminal/block_controller/traits.rs | 10 +- .../terminal/connections/connection_config.rs | 30 +-- .../src/terminal/connections/local_pty.rs | 2 +- .../terminal/connections/ssh_connection.rs | 72 +++--- .../terminal/connections/ssh_shell_proc.rs | 10 +- .../terminal/connections/wsl_connection.rs | 25 +- .../src/terminal/integration/osc_parser.rs | 4 +- src-tauri/src/terminal/integration/resync.rs | 20 +- .../src/terminal/integration/shell_scripts.rs | 22 +- .../src/terminal/persistence/block_file.rs | 38 +-- .../src/terminal/persistence/session_store.rs | 66 +++--- src-tauri/src/terminal/session_manager.rs | 9 +- .../src/translator/kiro/anthropic/request.rs | 2 +- .../src/translator/kiro/openai/request.rs | 2 +- src-tauri/src/translator/traits.rs | 4 +- src-tauri/src/tray/sync.rs | 4 +- src-tauri/src/voice/asr_service.rs | 41 ++-- src-tauri/src/voice/commands.rs | 8 +- src-tauri/src/voice/output_service.rs | 10 +- src-tauri/src/voice/processor.rs | 10 +- src-tauri/src/voice/recording_service.rs | 18 +- src-tauri/src/voice/shortcut.rs | 36 +-- src-tauri/src/voice/window.rs | 6 +- src-tauri/src/websocket/handler.rs | 12 +- src-tauri/src/websocket/processor.rs | 7 +- src-tauri/src/websocket/stream.rs | 2 +- src-tauri/src/websocket/tests.rs | 6 +- src-tauri/src/workspace/manager.rs | 134 ++++------- src-tauri/src/workspace/types.rs | 4 +- src-tauri/tests/api_key_provider_tests.rs | 16 +- src-tauri/tests/end_to_end_tests.rs | 2 +- .../agent/chat/components/EmptyState.tsx | 1 + .../Inputbar/components/CharacterMention.tsx | 206 ++++++++++++++++ .../Inputbar/components/InputbarCore.tsx | 8 +- .../agent/chat/components/Inputbar/index.tsx | 20 ++ .../agent/chat/components/ProjectSelector.tsx | 223 ++++++++++++++++++ .../agent/chat/hooks/useContentSync.ts | 102 ++++++++ src/components/agent/chat/index.tsx | 125 +++++++++- src/components/projects/ProjectsPage.tsx | 35 ++- src/components/ui/command.tsx | 153 ++++++++++++ 231 files changed, 3542 insertions(+), 3217 deletions(-) create mode 100644 src-tauri/proptest-regressions/config/tests.txt create mode 100644 src/components/agent/chat/components/Inputbar/components/CharacterMention.tsx create mode 100644 src/components/agent/chat/components/ProjectSelector.tsx create mode 100644 src/components/agent/chat/hooks/useContentSync.ts create mode 100644 src/components/ui/command.tsx diff --git a/package.json b/package.json index 176abcb84..effa62879 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "proxycast", "private": true, - "version": "0.53.0", + "version": "0.54.0", "type": "module", "repository": { "type": "git", @@ -62,6 +62,7 @@ "@xterm/xterm": "^6.0.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "date-fns": "^4.1.0", "dayjs": "^1.11.19", "fabric": "^5.5.2", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c1af2fa10..f0e801ac7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -202,7 +202,7 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "aster" -version = "0.7.0" +version = "0.7.1" dependencies = [ "ahash", "anyhow", @@ -2112,7 +2112,7 @@ dependencies = [ "dtoa-short", "itoa", "matches", - "phf 0.10.1", + "phf 0.8.0", "proc-macro2", "quote", "smallvec", @@ -2128,7 +2128,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.3", + "phf 0.8.0", "smallvec", ] @@ -3988,7 +3988,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.57.0", + "windows-core 0.56.0", ] [[package]] @@ -5307,7 +5307,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.114", @@ -6024,7 +6024,9 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ + "phf_macros 0.8.0", "phf_shared 0.8.0", + "proc-macro-hack", ] [[package]] @@ -6033,9 +6035,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_macros 0.10.0", "phf_shared 0.10.0", - "proc-macro-hack", ] [[package]] @@ -6139,12 +6139,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.8.0", + "phf_shared 0.8.0", "proc-macro-hack", "proc-macro2", "quote", @@ -6545,7 +6545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.114", @@ -6553,7 +6553,7 @@ dependencies = [ [[package]] name = "proxycast" -version = "0.53.0" +version = "0.54.0" dependencies = [ "anyhow", "arboard", @@ -6635,7 +6635,7 @@ dependencies = [ [[package]] name = "proxycast-core" -version = "0.53.0" +version = "0.54.0" dependencies = [ "chrono", "dirs 5.0.1", @@ -6651,7 +6651,7 @@ dependencies = [ [[package]] name = "proxycast-infra" -version = "0.53.0" +version = "0.54.0" dependencies = [ "chrono", "dashmap 5.5.3", @@ -7969,7 +7969,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" dependencies = [ - "dirs 6.0.0", + "dirs 4.0.0", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e6952942c..a20f58e76 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.package] -version = "0.53.0" +version = "0.54.0" edition = "2021" authors = ["you"] repository = "https://github.com/aiclientproxy/proxycast" @@ -103,9 +103,9 @@ enigo = "0.3" # Aster Agent Framework # 开发时使用本地 aster-rust,CI/CD 使用远程 GitHub 仓库 # 本地开发: path = "../../../astercloud/aster-rust/crates/aster" (相对 src-tauri/) -# CI/CD: git = "https://github.com/astercloud/aster-rust", tag = "v0.7.0" +# CI/CD: git = "https://github.com/astercloud/aster-rust", tag = "v0.7.1" # aster = { version = "0.5.1", path = "../../../astercloud/aster-rust/crates/aster" } -aster = { git = "https://github.com/astercloud/aster-rust", tag = "v0.7.0" } +aster = { git = "https://github.com/astercloud/aster-rust", tag = "v0.7.1" } # Tauri @@ -164,7 +164,7 @@ version = "2.4" [package] name = "proxycast" -version = "0.53.0" +version = "0.54.0" description = "AI API Proxy Desktop App" authors = ["you"] edition = "2021" diff --git a/src-tauri/crates/core/src/logger.rs b/src-tauri/crates/core/src/logger.rs index 6f3ce21ad..69af7b85c 100644 --- a/src-tauri/crates/core/src/logger.rs +++ b/src-tauri/crates/core/src/logger.rs @@ -161,7 +161,7 @@ impl LogStore { self.prune_old_logs(path); } - fn prune_old_logs(&self, path: &PathBuf) { + fn prune_old_logs(&self, path: &std::path::Path) { let Some(dir) = path.parent() else { return; }; diff --git a/src-tauri/crates/core/src/models/kiro_fingerprint.rs b/src-tauri/crates/core/src/models/kiro_fingerprint.rs index c3b25fba5..00628f642 100644 --- a/src-tauri/crates/core/src/models/kiro_fingerprint.rs +++ b/src-tauri/crates/core/src/models/kiro_fingerprint.rs @@ -38,8 +38,7 @@ impl KiroFingerprintStore { .join("proxycast"); if !app_data_dir.exists() { - fs::create_dir_all(&app_data_dir) - .map_err(|e| format!("创建应用数据目录失败: {}", e))?; + fs::create_dir_all(&app_data_dir).map_err(|e| format!("创建应用数据目录失败: {e}"))?; } Ok(app_data_dir.join("kiro_fingerprints.json")) @@ -54,18 +53,18 @@ impl KiroFingerprintStore { } let content = - fs::read_to_string(&path).map_err(|e| format!("读取指纹存储文件失败: {}", e))?; + fs::read_to_string(&path).map_err(|e| format!("读取指纹存储文件失败: {e}"))?; - serde_json::from_str(&content).map_err(|e| format!("解析指纹存储文件失败: {}", e)) + serde_json::from_str(&content).map_err(|e| format!("解析指纹存储文件失败: {e}")) } /// 保存到文件 pub fn save(&self) -> Result<(), String> { let path = Self::get_storage_path()?; let content = - serde_json::to_string_pretty(self).map_err(|e| format!("序列化指纹存储失败: {}", e))?; + serde_json::to_string_pretty(self).map_err(|e| format!("序列化指纹存储失败: {e}"))?; - fs::write(&path, content).map_err(|e| format!("写入指纹存储文件失败: {}", e)) + fs::write(&path, content).map_err(|e| format!("写入指纹存储文件失败: {e}")) } /// 获取凭证的指纹绑定 @@ -132,7 +131,7 @@ fn generate_stable_machine_id( hasher.update(seed.as_bytes()); let result = hasher.finalize(); - let hex = format!("{:x}", result); + let hex = format!("{result:x}"); format!( "{}-{}-{}-{}-{}", &hex[0..8], diff --git a/src-tauri/crates/core/src/models/model_registry.rs b/src-tauri/crates/core/src/models/model_registry.rs index e41bb0cc0..9b3ca74f9 100644 --- a/src-tauri/crates/core/src/models/model_registry.rs +++ b/src-tauri/crates/core/src/models/model_registry.rs @@ -86,7 +86,7 @@ impl std::str::FromStr for ModelStatus { "beta" => Ok(Self::Beta), "deprecated" => Ok(Self::Deprecated), "legacy" => Ok(Self::Legacy), - _ => Err(format!("Unknown model status: {}", s)), + _ => Err(format!("Unknown model status: {s}")), } } } @@ -124,7 +124,7 @@ impl std::str::FromStr for ModelTier { "mini" => Ok(Self::Mini), "pro" => Ok(Self::Pro), "max" => Ok(Self::Max), - _ => Err(format!("Unknown model tier: {}", s)), + _ => Err(format!("Unknown model tier: {s}")), } } } @@ -165,7 +165,7 @@ impl std::str::FromStr for ModelSource { "models.dev" | "modelsdev" => Ok(Self::ModelsDev), "local" => Ok(Self::Local), "custom" => Ok(Self::Custom), - _ => Err(format!("Unknown model source: {}", s)), + _ => Err(format!("Unknown model source: {s}")), } } } @@ -300,7 +300,7 @@ impl UserModelPreference { } /// 模型同步状态 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ModelSyncState { pub last_sync_at: Option, pub model_count: u32, @@ -308,17 +308,6 @@ pub struct ModelSyncState { pub last_error: Option, } -impl Default for ModelSyncState { - fn default() -> Self { - Self { - last_sync_at: None, - model_count: 0, - is_syncing: false, - last_error: None, - } - } -} - // Provider Alias 相关类型 /// 单个模型别名映射 diff --git a/src-tauri/crates/core/src/models/route_model.rs b/src-tauri/crates/core/src/models/route_model.rs index 1c3a287ae..aad72620a 100644 --- a/src-tauri/crates/core/src/models/route_model.rs +++ b/src-tauri/crates/core/src/models/route_model.rs @@ -56,7 +56,7 @@ impl RouteInfo { "openai" => format!("/{}/v1/chat/completions", self.selector), _ => return, }; - let url = format!("{}{}", base_url, path); + let url = format!("{base_url}{path}"); self.endpoints.push(RouteEndpoint { path, protocol: protocol.to_string(), @@ -81,11 +81,10 @@ impl RouteInfo { model, format!( r#"{{ - "model": "{}", + "model": "{model}", "max_tokens": 1024, "messages": [{{"role": "user", "content": "Hello!"}}] -}}"#, - model +}}"# ), ) } @@ -101,10 +100,9 @@ impl RouteInfo { model, format!( r#"{{ - "model": "{}", + "model": "{model}", "messages": [{{"role": "user", "content": "Hello!"}}] -}}"#, - model +}}"# ), ) } diff --git a/src-tauri/crates/infra/src/resilience/failover.rs b/src-tauri/crates/infra/src/resilience/failover.rs index 5a84bfd27..c55217e27 100644 --- a/src-tauri/crates/infra/src/resilience/failover.rs +++ b/src-tauri/crates/infra/src/resilience/failover.rs @@ -131,7 +131,7 @@ impl FailoverResult { switched: true, new_provider: Some(new_provider), failure_type, - message: format!("已切换到 Provider: {}", new_provider), + message: format!("已切换到 Provider: {new_provider}"), } } @@ -206,7 +206,7 @@ impl Failover { if !should_switch { return FailoverResult::not_switched( failure_type, - &format!("不在 {:?} 故障时切换", failure_type), + &format!("不在 {failure_type:?} 故障时切换"), ); } @@ -347,7 +347,7 @@ impl FailoverManager { if !should_switch { return FailoverResult::not_switched( failure_type, - &format!("不在 {:?} 故障时切换", failure_type), + &format!("不在 {failure_type:?} 故障时切换"), ); } diff --git a/src-tauri/crates/infra/src/resilience/timeout.rs b/src-tauri/crates/infra/src/resilience/timeout.rs index 91644ab56..c866f4c46 100644 --- a/src-tauri/crates/infra/src/resilience/timeout.rs +++ b/src-tauri/crates/infra/src/resilience/timeout.rs @@ -92,21 +92,13 @@ impl std::fmt::Display for TimeoutError { timeout_ms, elapsed_ms, } => { - write!( - f, - "请求超时: 配置 {}ms, 已耗时 {}ms", - timeout_ms, elapsed_ms - ) + write!(f, "请求超时: 配置 {timeout_ms}ms, 已耗时 {elapsed_ms}ms") } TimeoutError::StreamIdleTimeout { timeout_ms, idle_ms, } => { - write!( - f, - "流式响应空闲超时: 配置 {}ms, 空闲 {}ms", - timeout_ms, idle_ms - ) + write!(f, "流式响应空闲超时: 配置 {timeout_ms}ms, 空闲 {idle_ms}ms") } TimeoutError::Cancelled => { write!(f, "操作已取消") diff --git a/src-tauri/crates/infra/src/telemetry/logger.rs b/src-tauri/crates/infra/src/telemetry/logger.rs index 1815c77d1..d0337df7c 100644 --- a/src-tauri/crates/infra/src/telemetry/logger.rs +++ b/src-tauri/crates/infra/src/telemetry/logger.rs @@ -29,10 +29,10 @@ pub enum LoggerError { impl std::fmt::Display for LoggerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - LoggerError::Io(e) => write!(f, "IO 错误: {}", e), - LoggerError::Serialization(e) => write!(f, "序列化错误: {}", e), - LoggerError::NotFound(id) => write!(f, "日志未找到: {}", id), - LoggerError::DirectoryCreation(msg) => write!(f, "日志目录创建失败: {}", msg), + LoggerError::Io(e) => write!(f, "IO 错误: {e}"), + LoggerError::Serialization(e) => write!(f, "序列化错误: {e}"), + LoggerError::NotFound(id) => write!(f, "日志未找到: {id}"), + LoggerError::DirectoryCreation(msg) => write!(f, "日志目录创建失败: {msg}"), } } } @@ -107,7 +107,7 @@ impl RequestLogger { // 创建日志目录 fs::create_dir_all(&log_dir).map_err(|e| { - LoggerError::DirectoryCreation(format!("无法创建日志目录 {:?}: {}", log_dir, e)) + LoggerError::DirectoryCreation(format!("无法创建日志目录 {log_dir:?}: {e}")) })?; let logger = Self { @@ -316,7 +316,7 @@ impl RequestLogger { let mut file = OpenOptions::new().create(true).append(true).open(&path)?; let json = serde_json::to_string(log)?; - writeln!(file, "{}", json)?; + writeln!(file, "{json}")?; } Ok(()) @@ -325,7 +325,7 @@ impl RequestLogger { /// 如果需要则轮转日志文件 fn rotate_log_file_if_needed(&self) -> Result<(), LoggerError> { let today = Utc::now().format("%Y-%m-%d").to_string(); - let expected_file = self.log_dir.join(format!("requests_{}.jsonl", today)); + let expected_file = self.log_dir.join(format!("requests_{today}.jsonl")); let needs_rotation = { let current = self.current_log_file.read(); @@ -370,9 +370,7 @@ impl RequestLogger { fn find_next_log_file(&self, date: &str) -> Result { let mut index = 1; loop { - let file = self - .log_dir - .join(format!("requests_{}_{}.jsonl", date, index)); + let file = self.log_dir.join(format!("requests_{date}_{index}.jsonl")); if !file.exists() || file .metadata() diff --git a/src-tauri/crates/infra/src/telemetry/tokens.rs b/src-tauri/crates/infra/src/telemetry/tokens.rs index b6220eeb3..1b20d2534 100644 --- a/src-tauri/crates/infra/src/telemetry/tokens.rs +++ b/src-tauri/crates/infra/src/telemetry/tokens.rs @@ -525,7 +525,7 @@ impl std::fmt::Display for TokenEstimatorError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TokenEstimatorError::InitializationError(msg) => { - write!(f, "Token 估算器初始化失败: {}", msg) + write!(f, "Token 估算器初始化失败: {msg}") } } } diff --git a/src-tauri/crates/voice-core/src/asr_client/openai.rs b/src-tauri/crates/voice-core/src/asr_client/openai.rs index dc108258e..689ab378c 100644 --- a/src-tauri/crates/voice-core/src/asr_client/openai.rs +++ b/src-tauri/crates/voice-core/src/asr_client/openai.rs @@ -84,8 +84,7 @@ impl AsrClient for OpenAIWhisperClient { let status = response.status(); let body = response.text().await.unwrap_or_default(); return Err(VoiceError::AsrError(format!( - "OpenAI API 错误: {} - {}", - status, body + "OpenAI API 错误: {status} - {body}" ))); } diff --git a/src-tauri/crates/voice-core/src/asr_client/xunfei.rs b/src-tauri/crates/voice-core/src/asr_client/xunfei.rs index 214b90941..20931065e 100644 --- a/src-tauri/crates/voice-core/src/asr_client/xunfei.rs +++ b/src-tauri/crates/voice-core/src/asr_client/xunfei.rs @@ -104,7 +104,7 @@ impl XunfeiClient { tracing::debug!("讯飞鉴权 - api_secret 长度: {}", self.api_secret.len()); // 构建签名原文 - let signature_origin = format!("host: {}\ndate: {}\nGET {} HTTP/1.1", host, date, path); + let signature_origin = format!("host: {host}\ndate: {date}\nGET {path} HTTP/1.1"); tracing::debug!("讯飞鉴权 - signature_origin:\n{}", signature_origin); // HMAC-SHA256 签名 @@ -292,7 +292,7 @@ impl AsrClient for XunfeiClient { tracing::info!("正在连接讯飞 WebSocket..."); let (ws_stream, response) = connect_async(&url).await.map_err(|e| { tracing::error!("讯飞 WebSocket 连接失败: {:?}", e); - VoiceError::NetworkError(format!("WebSocket 连接失败: {}", e)) + VoiceError::NetworkError(format!("WebSocket 连接失败: {e}")) })?; tracing::info!( @@ -406,7 +406,7 @@ impl AsrClient for XunfeiClient { let json = match serde_json::to_string(&request) { Ok(j) => j, Err(e) => { - send_error = Some(VoiceError::AsrError(format!("序列化请求失败: {}", e))); + send_error = Some(VoiceError::AsrError(format!("序列化请求失败: {e}"))); break; } }; @@ -422,7 +422,7 @@ impl AsrClient for XunfeiClient { } Err(e) => { tracing::error!("发送第 {} 帧失败: {}", i, e); - send_error = Some(VoiceError::NetworkError(format!("发送数据失败: {}", e))); + send_error = Some(VoiceError::NetworkError(format!("发送数据失败: {e}"))); break; } } @@ -440,7 +440,7 @@ impl AsrClient for XunfeiClient { match tokio::time::timeout(tokio::time::Duration::from_secs(30), receive_task).await { Ok(Ok(responses)) => responses, Ok(Err(e)) => { - return Err(VoiceError::AsrError(format!("接收任务失败: {}", e))); + return Err(VoiceError::AsrError(format!("接收任务失败: {e}"))); } Err(_) => { return Err(VoiceError::AsrError("等待识别结果超时".to_string())); diff --git a/src-tauri/proptest-regressions/config/tests.txt b/src-tauri/proptest-regressions/config/tests.txt new file mode 100644 index 000000000..848ff117f --- /dev/null +++ b/src-tauri/proptest-regressions/config/tests.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 690e03d4ddd600c7175d123787d49a9afacf6f13642e9f7ae0e9e766a2ba93c2 # shrinks to provider = "qwen" diff --git a/src-tauri/src/agent/aster_agent.rs b/src-tauri/src/agent/aster_agent.rs index 3926ea5e6..980c5fe02 100644 --- a/src-tauri/src/agent/aster_agent.rs +++ b/src-tauri/src/agent/aster_agent.rs @@ -4,11 +4,11 @@ //! 处理消息发送、事件流转换和会话管理 use crate::agent::aster_state::{AsterAgentState, SessionConfigBuilder}; +use crate::database::dao::agent::AgentDao; use crate::database::DbConnection; use aster::conversation::message::Message; -use aster::session::SessionManager; +use chrono::Utc; use futures::StreamExt; -use std::path::PathBuf; use tauri::{AppHandle, Emitter}; /// Aster Agent 包装器 @@ -78,7 +78,7 @@ impl AsterAgentWrapper { // 发送错误事件 let error_event = crate::agent::event_converter::TauriAgentEvent::Error { - message: format!("Stream error: {}", e), + message: format!("Stream error: {e}"), }; let _ = app.emit(&event_name, &error_event); } @@ -93,10 +93,10 @@ impl AsterAgentWrapper { Err(e) => { // 发送错误事件并返回错误 let error_event = crate::agent::event_converter::TauriAgentEvent::Error { - message: format!("Agent error: {}", e), + message: format!("Agent error: {e}"), }; let _ = app.emit(&event_name, &error_event); - return Err(format!("Agent error: {}", e)); + return Err(format!("Agent error: {e}")); } } @@ -113,60 +113,74 @@ impl AsterAgentWrapper { state.cancel_session(session_id).await } - /// 创建新会话 - pub async fn create_session( - working_dir: Option, - name: Option, - ) -> Result { - let dir = working_dir - .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))); - let session_name = name.unwrap_or_else(|| "New Session".to_string()); + /// 创建新会话 - 使用 ProxyCast 数据库 + pub fn create_session_sync(db: &DbConnection, name: Option) -> Result { + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; + let session_name = name.unwrap_or_else(|| "新对话".to_string()); + let session_id = uuid::Uuid::new_v4().to_string(); + let now = Utc::now().to_rfc3339(); - let session = - SessionManager::create_session(dir, session_name, aster::session::SessionType::User) - .await - .map_err(|e| format!("Failed to create session: {}", e))?; + let session = crate::agent::types::AgentSession { + id: session_id.clone(), + model: "agent:default".to_string(), + messages: Vec::new(), + system_prompt: None, + title: Some(session_name), + created_at: now.clone(), + updated_at: now, + }; - Ok(session.id) + AgentDao::create_session(&conn, &session).map_err(|e| format!("创建会话失败: {e}"))?; + + Ok(session_id) } - /// 列出所有会话 - pub async fn list_sessions() -> Result, String> { - let sessions = SessionManager::list_sessions() - .await - .map_err(|e| format!("Failed to list sessions: {}", e))?; + /// 列出所有会话 - 使用 ProxyCast 数据库 + pub fn list_sessions_sync(db: &DbConnection) -> Result, String> { + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; + + let sessions = + AgentDao::list_sessions(&conn).map_err(|e| format!("获取会话列表失败: {e}"))?; Ok(sessions .into_iter() .map(|s| SessionInfo { id: s.id, - name: s.name, - created_at: s.created_at.timestamp(), - updated_at: s.updated_at.timestamp(), + name: s.title.unwrap_or_else(|| "未命名".to_string()), + created_at: chrono::DateTime::parse_from_rfc3339(&s.created_at) + .map(|dt| dt.timestamp()) + .unwrap_or(0), + updated_at: chrono::DateTime::parse_from_rfc3339(&s.updated_at) + .map(|dt| dt.timestamp()) + .unwrap_or(0), }) .collect()) } - /// 获取会话详情 - pub async fn get_session(session_id: &str) -> Result { - let session = SessionManager::get_session(session_id, true) - .await - .map_err(|e| format!("Failed to get session: {}", e))?; + /// 获取会话详情 - 使用 ProxyCast 数据库 + pub fn get_session_sync(db: &DbConnection, session_id: &str) -> Result { + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; + + let session = AgentDao::get_session(&conn, session_id) + .map_err(|e| format!("获取会话失败: {e}"))? + .ok_or_else(|| format!("会话不存在: {session_id}"))?; + + let messages = + AgentDao::get_messages(&conn, session_id).map_err(|e| format!("获取消息失败: {e}"))?; Ok(SessionDetail { id: session.id, - name: session.name, - created_at: session.created_at.timestamp(), - updated_at: session.updated_at.timestamp(), - messages: session - .conversation - .map(|c| { - c.messages() - .iter() - .map(|m| crate::agent::event_converter::convert_to_tauri_message(m)) - .collect() - }) - .unwrap_or_default(), + name: session.title.unwrap_or_else(|| "未命名".to_string()), + created_at: chrono::DateTime::parse_from_rfc3339(&session.created_at) + .map(|dt| dt.timestamp()) + .unwrap_or(0), + updated_at: chrono::DateTime::parse_from_rfc3339(&session.updated_at) + .map(|dt| dt.timestamp()) + .unwrap_or(0), + messages: messages + .into_iter() + .map(|m| convert_agent_message(&m)) + .collect(), }) } } @@ -190,6 +204,40 @@ pub struct SessionDetail { pub messages: Vec, } +/// 将 AgentMessage 转换为 TauriMessage +fn convert_agent_message( + msg: &crate::agent::types::AgentMessage, +) -> crate::agent::event_converter::TauriMessage { + use crate::agent::event_converter::{TauriMessage, TauriMessageContent}; + use crate::agent::types::MessageContent; + + let content = match &msg.content { + MessageContent::Text(text) => vec![TauriMessageContent::Text { text: text.clone() }], + MessageContent::Parts(parts) => parts + .iter() + .filter_map(|p| { + if let crate::agent::types::ContentPart::Text { text } = p { + Some(TauriMessageContent::Text { text: text.clone() }) + } else { + None + } + }) + .collect(), + }; + + // 解析时间戳 + let timestamp = chrono::DateTime::parse_from_rfc3339(&msg.timestamp) + .map(|dt| dt.timestamp()) + .unwrap_or(0); + + TauriMessage { + id: None, + role: msg.role.clone(), + content, + timestamp, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/src/agent/aster_state.rs b/src-tauri/src/agent/aster_state.rs index 79b5e66d7..c70ce592b 100644 --- a/src-tauri/src/agent/aster_state.rs +++ b/src-tauri/src/agent/aster_state.rs @@ -89,10 +89,18 @@ impl AsterAgentState { if agent_guard.is_none() { // 创建 SessionStore let session_store = Arc::new(ProxyCastSessionStore::new(db.clone())); + tracing::info!("[AsterAgent] 创建 ProxyCastSessionStore 成功"); // 创建 Agent 并注入 SessionStore let agent = Agent::new().with_session_store(session_store); + // 验证 session_store 是否被正确设置 + let has_store = agent.session_store().is_some(); + tracing::info!( + "[AsterAgent] Agent 创建完成,session_store 已设置: {}", + has_store + ); + // 使用异步方法设置 ProxyCast 专属身份 let identity = Self::create_proxycast_identity(); agent.set_identity(identity).await; @@ -101,6 +109,8 @@ impl AsterAgentState { tracing::info!( "[AsterAgent] Agent 初始化成功,已注入 ProxyCastSessionStore 和 ProxyCast 身份" ); + } else { + tracing::debug!("[AsterAgent] Agent 已初始化,跳过"); } Ok(()) } @@ -159,12 +169,12 @@ impl AsterAgentState { // 创建 ModelConfig let model_config = ModelConfig::new(&config.model_name) - .map_err(|e| format!("创建 ModelConfig 失败: {}", e))?; + .map_err(|e| format!("创建 ModelConfig 失败: {e}"))?; // 创建 Provider let provider = aster::providers::create(&config.provider_name, model_config) .await - .map_err(|e| format!("创建 Provider 失败: {}", e))?; + .map_err(|e| format!("创建 Provider 失败: {e}"))?; // 更新 Agent 的 Provider let agent_guard = self.agent.read().await; @@ -172,7 +182,7 @@ impl AsterAgentState { agent .update_provider(provider, session_id) .await - .map_err(|e| format!("更新 Provider 失败: {}", e))?; + .map_err(|e| format!("更新 Provider 失败: {e}"))?; } // 保存当前配置 @@ -212,12 +222,12 @@ impl AsterAgentState { .credential_bridge .select_and_configure(db, provider_type, model) .await - .map_err(|e| format!("从凭证池选择凭证失败: {}", e))?; + .map_err(|e| format!("从凭证池选择凭证失败: {e}"))?; // 创建 Provider let provider = create_aster_provider(&aster_config) .await - .map_err(|e| format!("创建 Provider 失败: {}", e))?; + .map_err(|e| format!("创建 Provider 失败: {e}"))?; // 更新 Agent 的 Provider let agent_guard = self.agent.read().await; @@ -225,7 +235,7 @@ impl AsterAgentState { agent .update_provider(provider, session_id) .await - .map_err(|e| format!("更新 Provider 失败: {}", e))?; + .map_err(|e| format!("更新 Provider 失败: {e}"))?; } // 保存当前配置 @@ -446,36 +456,6 @@ pub mod message_helpers { } } -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_aster_state_init() { - let state = AsterAgentState::new(); - assert!(!state.is_initialized().await); - - #[allow(deprecated)] - state.init_agent().await.unwrap(); - assert!(state.is_initialized().await); - } - - #[tokio::test] - async fn test_cancel_token() { - let state = AsterAgentState::new(); - let session_id = "test-session"; - - let token = state.create_cancel_token(session_id).await; - assert!(!token.is_cancelled()); - - assert!(state.cancel_session(session_id).await); - assert!(token.is_cancelled()); - - state.remove_cancel_token(session_id).await; - assert!(!state.cancel_session(session_id).await); - } -} - // ============================================================================= // ProxyCast Agent 身份提示词 // ============================================================================= @@ -505,3 +485,33 @@ ProxyCast 是一个 AI 代理服务应用,帮助用户: - 友好但不啰嗦,像经验丰富的技术伙伴 - 遇到问题时,先分析原因再提供方案 "#; + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_aster_state_init() { + let state = AsterAgentState::new(); + assert!(!state.is_initialized().await); + + #[allow(deprecated)] + state.init_agent().await.unwrap(); + assert!(state.is_initialized().await); + } + + #[tokio::test] + async fn test_cancel_token() { + let state = AsterAgentState::new(); + let session_id = "test-session"; + + let token = state.create_cancel_token(session_id).await; + assert!(!token.is_cancelled()); + + assert!(state.cancel_session(session_id).await); + assert!(token.is_cancelled()); + + state.remove_cancel_token(session_id).await; + assert!(!state.cancel_session(session_id).await); + } +} diff --git a/src-tauri/src/agent/credential_bridge.rs b/src-tauri/src/agent/credential_bridge.rs index 3b816ae06..f0e6e2ded 100644 --- a/src-tauri/src/agent/credential_bridge.rs +++ b/src-tauri/src/agent/credential_bridge.rs @@ -35,11 +35,11 @@ pub enum CredentialBridgeError { impl std::fmt::Display for CredentialBridgeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::NoCredentials(msg) => write!(f, "没有可用凭证: {}", msg), - Self::UnsupportedCredentialType(msg) => write!(f, "不支持的凭证类型: {}", msg), - Self::ProviderCreationFailed(msg) => write!(f, "Provider 创建失败: {}", msg), - Self::TokenRefreshFailed(msg) => write!(f, "Token 刷新失败: {}", msg), - Self::DatabaseError(msg) => write!(f, "数据库错误: {}", msg), + Self::NoCredentials(msg) => write!(f, "没有可用凭证: {msg}"), + Self::UnsupportedCredentialType(msg) => write!(f, "不支持的凭证类型: {msg}"), + Self::ProviderCreationFailed(msg) => write!(f, "Provider 创建失败: {msg}"), + Self::TokenRefreshFailed(msg) => write!(f, "Token 刷新失败: {msg}"), + Self::DatabaseError(msg) => write!(f, "数据库错误: {msg}"), } } } @@ -112,11 +112,10 @@ impl CredentialBridge { None, ) .await - .map_err(|e| CredentialBridgeError::DatabaseError(e))? + .map_err(CredentialBridgeError::DatabaseError)? .ok_or_else(|| { CredentialBridgeError::NoCredentials(format!( - "没有找到 {} 类型的可用凭证", - provider_type + "没有找到 {provider_type} 类型的可用凭证" )) })?; @@ -229,7 +228,7 @@ impl CredentialBridge { .load_credentials_from_path(creds_path) .await .map_err(|e| { - CredentialBridgeError::TokenRefreshFailed(format!("加载 Kiro 凭证失败: {}", e)) + CredentialBridgeError::TokenRefreshFailed(format!("加载 Kiro 凭证失败: {e}")) })?; // 检查 token 是否过期,如果过期则刷新 @@ -238,14 +237,14 @@ impl CredentialBridge { self.pool_service .refresh_kiro_token(creds_path) .await - .map_err(|e| CredentialBridgeError::TokenRefreshFailed(e))?; + .map_err(CredentialBridgeError::TokenRefreshFailed)?; // 重新加载凭证 provider .load_credentials_from_path(creds_path) .await .map_err(|e| { - CredentialBridgeError::TokenRefreshFailed(format!("重新加载凭证失败: {}", e)) + CredentialBridgeError::TokenRefreshFailed(format!("重新加载凭证失败: {e}")) })?; } @@ -257,12 +256,11 @@ impl CredentialBridge { /// 获取通用 OAuth Token async fn get_oauth_token(&self, creds_path: &str) -> Result { let content = std::fs::read_to_string(creds_path).map_err(|e| { - CredentialBridgeError::TokenRefreshFailed(format!("读取凭证文件失败: {}", e)) + CredentialBridgeError::TokenRefreshFailed(format!("读取凭证文件失败: {e}")) })?; - let creds: serde_json::Value = serde_json::from_str(&content).map_err(|e| { - CredentialBridgeError::TokenRefreshFailed(format!("解析凭证失败: {}", e)) - })?; + let creds: serde_json::Value = serde_json::from_str(&content) + .map_err(|e| CredentialBridgeError::TokenRefreshFailed(format!("解析凭证失败: {e}")))?; creds["access_token"] .as_str() @@ -281,11 +279,11 @@ impl CredentialBridge { .load_credentials_from_path(creds_path) .await .map_err(|e| { - CredentialBridgeError::TokenRefreshFailed(format!("加载 Codex 凭证失败: {}", e)) + CredentialBridgeError::TokenRefreshFailed(format!("加载 Codex 凭证失败: {e}")) })?; provider.ensure_valid_token().await.map_err(|e| { - CredentialBridgeError::TokenRefreshFailed(format!("获取 Codex token 失败: {}", e)) + CredentialBridgeError::TokenRefreshFailed(format!("获取 Codex token 失败: {e}")) }) } @@ -293,7 +291,7 @@ impl CredentialBridge { pub fn record_usage(&self, db: &DbConnection, uuid: &str) -> Result<(), CredentialBridgeError> { self.pool_service .record_usage(db, uuid) - .map_err(|e| CredentialBridgeError::DatabaseError(e)) + .map_err(CredentialBridgeError::DatabaseError) } /// 标记凭证为健康 @@ -305,7 +303,7 @@ impl CredentialBridge { ) -> Result<(), CredentialBridgeError> { self.pool_service .mark_healthy(db, uuid, model) - .map_err(|e| CredentialBridgeError::DatabaseError(e)) + .map_err(CredentialBridgeError::DatabaseError) } /// 标记凭证为不健康 @@ -317,7 +315,7 @@ impl CredentialBridge { ) -> Result<(), CredentialBridgeError> { self.pool_service .mark_unhealthy(db, uuid, error) - .map_err(|e| CredentialBridgeError::DatabaseError(e)) + .map_err(CredentialBridgeError::DatabaseError) } } @@ -332,14 +330,14 @@ pub async fn create_aster_provider( // 创建 ModelConfig let model_config = ModelConfig::new(&config.model_name).map_err(|e| { - CredentialBridgeError::ProviderCreationFailed(format!("创建 ModelConfig 失败: {}", e)) + CredentialBridgeError::ProviderCreationFailed(format!("创建 ModelConfig 失败: {e}")) })?; // 创建 Provider aster::providers::create(&config.provider_name, model_config) .await .map_err(|e| { - CredentialBridgeError::ProviderCreationFailed(format!("创建 Provider 失败: {}", e)) + CredentialBridgeError::ProviderCreationFailed(format!("创建 Provider 失败: {e}")) }) } diff --git a/src-tauri/src/agent/event_converter.rs b/src-tauri/src/agent/event_converter.rs index 86c6215ee..a469883d6 100644 --- a/src-tauri/src/agent/event_converter.rs +++ b/src-tauri/src/agent/event_converter.rs @@ -205,7 +205,7 @@ fn convert_message(message: Message) -> Vec { } Err(e) => { events.push(TauriAgentEvent::Error { - message: format!("Invalid tool call: {}", e), + message: format!("Invalid tool call: {e}"), }); } }, @@ -302,7 +302,7 @@ fn convert_message(message: Message) -> Vec { } Err(e) => { events.push(TauriAgentEvent::Error { - message: format!("Invalid frontend tool call: {}", e), + message: format!("Invalid frontend tool call: {e}"), }); } }, @@ -320,7 +320,7 @@ pub fn convert_to_tauri_message(message: &Message) -> TauriMessage { let content = message .content .iter() - .filter_map(|c| convert_message_content(c)) + .filter_map(convert_message_content) .collect(); TauriMessage { diff --git a/src-tauri/src/app/bootstrap.rs b/src-tauri/src/app/bootstrap.rs index 10edd70f0..ac397caaf 100644 --- a/src-tauri/src/app/bootstrap.rs +++ b/src-tauri/src/app/bootstrap.rs @@ -64,8 +64,8 @@ pub enum ConfigError { impl std::fmt::Display for ConfigError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ConfigError::LoadFailed(e) => write!(f, "配置加载失败: {}", e), - ConfigError::SaveFailed(e) => write!(f, "配置保存失败: {}", e), + ConfigError::LoadFailed(e) => write!(f, "配置加载失败: {e}"), + ConfigError::SaveFailed(e) => write!(f, "配置保存失败: {e}"), ConfigError::InvalidHost => { write!( f, @@ -166,11 +166,10 @@ pub fn init_states(config: &Config) -> Result { let logs: LogState = Arc::new(RwLock::new(logger::LogStore::with_config(&config.logging))); // 数据库 - let db = database::init_database().map_err(|e| format!("数据库初始化失败: {}", e))?; + let db = database::init_database().map_err(|e| format!("数据库初始化失败: {e}"))?; // 服务状态 - let skill_service = - SkillService::new().map_err(|e| format!("SkillService 初始化失败: {}", e))?; + let skill_service = SkillService::new().map_err(|e| format!("SkillService 初始化失败: {e}"))?; let skill_service_state = SkillServiceState(Arc::new(skill_service)); let provider_pool_service = ProviderPoolService::new(); @@ -186,7 +185,7 @@ pub fn init_states(config: &Config) -> Result { let token_cache_service_state = TokenCacheServiceState(Arc::new(token_cache_service)); let machine_id_service = crate::services::machine_id_service::MachineIdService::new() - .map_err(|e| format!("MachineIdService 初始化失败: {}", e))?; + .map_err(|e| format!("MachineIdService 初始化失败: {e}"))?; let machine_id_service_state: MachineIdState = Arc::new(RwLock::new(machine_id_service)); let resilience_config_state = ResilienceConfigState::default(); @@ -259,7 +258,7 @@ pub fn init_states(config: &Config) -> Result { // 初始化会话文件存储 let session_files_storage = crate::session_files::SessionFileStorage::new() - .map_err(|e| format!("SessionFileStorage 初始化失败: {}", e))?; + .map_err(|e| format!("SessionFileStorage 初始化失败: {e}"))?; let session_files_state = SessionFilesState(std::sync::Mutex::new(session_files_storage)); // 初始化全局配置管理器 @@ -271,13 +270,13 @@ pub fn init_states(config: &Config) -> Result { { let conn = db.lock().expect("Failed to lock database"); database::dao::skills::SkillDao::init_default_skill_repos(&conn) - .map_err(|e| format!("初始化默认技能仓库失败: {}", e))?; + .map_err(|e| format!("初始化默认技能仓库失败: {e}"))?; } // 初始化上下文记忆服务 let context_memory_config = ContextMemoryConfig::default(); let context_memory_service = ContextMemoryService::new(context_memory_config) - .map_err(|e| format!("ContextMemoryService 初始化失败: {}", e))?; + .map_err(|e| format!("ContextMemoryService 初始化失败: {e}"))?; let context_memory_service_arc = Arc::new(context_memory_service); let context_memory_service_state = ContextMemoryServiceState(context_memory_service_arc.clone()); @@ -335,7 +334,7 @@ pub fn init_states(config: &Config) -> Result { /// 初始化插件安装器 fn init_plugin_installer() -> Result { - let db_path = database::get_db_path().map_err(|e| format!("获取数据库路径失败: {}", e))?; + let db_path = database::get_db_path().map_err(|e| format!("获取数据库路径失败: {e}"))?; let plugins_dir = dirs::data_dir() .unwrap_or_else(|| std::path::PathBuf::from(".")) .join("proxycast") @@ -366,7 +365,7 @@ fn init_plugin_installer() -> Result { fallback_temp_dir, &db_path, ) - .map_err(|e| format!("后备插件安装器初始化失败: {}", e))?; + .map_err(|e| format!("后备插件安装器初始化失败: {e}"))?; Ok(PluginInstallerState(Arc::new(RwLock::new(installer)))) } } @@ -398,7 +397,7 @@ fn init_telemetry( }; let shared_logger = Arc::new( telemetry::RequestLogger::new(log_rotation) - .map_err(|e| format!("RequestLogger 初始化失败: {}", e))?, + .map_err(|e| format!("RequestLogger 初始化失败: {e}"))?, ); let telemetry_state = crate::commands::telemetry_cmd::TelemetryState::with_shared( @@ -406,7 +405,7 @@ fn init_telemetry( shared_tokens.clone(), Some(shared_logger.clone()), ) - .map_err(|e| format!("TelemetryState 初始化失败: {}", e))?; + .map_err(|e| format!("TelemetryState 初始化失败: {e}"))?; Ok((telemetry_state, shared_stats, shared_tokens, shared_logger)) } @@ -483,22 +482,22 @@ fn init_flow_monitor( )); let flow_replayer_state = FlowReplayerState(flow_replayer); - let db_path = database::get_db_path().map_err(|e| format!("获取数据库路径失败: {}", e))?; + let db_path = database::get_db_path().map_err(|e| format!("获取数据库路径失败: {e}"))?; let session_manager = Arc::new( SessionManager::new(db_path.clone()) - .map_err(|e| format!("SessionManager 初始化失败: {}", e))?, + .map_err(|e| format!("SessionManager 初始化失败: {e}"))?, ); let session_manager_state = SessionManagerState(session_manager.clone()); let quick_filter_manager = Arc::new( QuickFilterManager::new(db_path.clone()) - .map_err(|e| format!("QuickFilterManager 初始化失败: {}", e))?, + .map_err(|e| format!("QuickFilterManager 初始化失败: {e}"))?, ); let quick_filter_manager_state = QuickFilterManagerState(quick_filter_manager); let bookmark_manager = Arc::new( - BookmarkManager::new(db_path).map_err(|e| format!("BookmarkManager 初始化失败: {}", e))?, + BookmarkManager::new(db_path).map_err(|e| format!("BookmarkManager 初始化失败: {e}"))?, ); let bookmark_manager_state = BookmarkManagerState(bookmark_manager); @@ -519,7 +518,7 @@ fn init_flow_monitor( let temp_dir = std::env::temp_dir().join("proxycast_flows"); let _ = std::fs::create_dir_all(&temp_dir); let temp_store = FlowFileStore::new(temp_dir, rotation_config) - .map_err(|e| format!("临时 FlowFileStore 初始化失败: {}", e))?; + .map_err(|e| format!("临时 FlowFileStore 初始化失败: {e}"))?; let query_service = FlowQueryService::new(flow_monitor.memory_store(), Arc::new(temp_store)); FlowQueryServiceState(Arc::new(query_service)) diff --git a/src-tauri/src/app/commands/config.rs b/src-tauri/src/app/commands/config.rs index 19b77f6db..6a69f6978 100644 --- a/src-tauri/src/app/commands/config.rs +++ b/src-tauri/src/app/commands/config.rs @@ -156,7 +156,7 @@ pub async fn set_endpoint_provider( .endpoint_providers .set_provider(&endpoint, provider.clone()) { - return Err(format!("未知的客户端类型: {}", endpoint)); + return Err(format!("未知的客户端类型: {endpoint}")); } config::save_config(&s.config).map_err(|e| e.to_string())?; @@ -181,10 +181,7 @@ pub async fn set_endpoint_provider( let provider_display = provider.as_deref().unwrap_or("默认"); logs.write().await.add( "info", - &format!( - "客户端 {} 的 Provider 已设置为: {}", - endpoint, provider_display - ), + &format!("客户端 {endpoint} 的 Provider 已设置为: {provider_display}"), ); tracing::info!( @@ -274,10 +271,7 @@ pub async fn update_provider_env_vars( // 未知类型,默认使用 ANTHROPIC_BASE_URL(因为大多数第三方 Provider 都是 Anthropic 兼容的) logs.write().await.add( "info", - &format!( - "Provider 类型 '{}' 使用默认 ANTHROPIC_BASE_URL", - provider_type - ), + &format!("Provider 类型 '{provider_type}' 使用默认 ANTHROPIC_BASE_URL"), ); let mut vars = vec![("ANTHROPIC_BASE_URL".to_string(), api_host.clone())]; if let Some(key) = api_key { @@ -324,7 +318,7 @@ pub async fn update_provider_env_vars( if let Err(e) = write_env_to_shell_config(&env_vars) { logs.write() .await - .add("warn", &format!("写入 shell 配置文件失败: {}", e)); + .add("warn", &format!("写入 shell 配置文件失败: {e}")); // 不中断流程 } diff --git a/src-tauri/src/app/runner.rs b/src-tauri/src/app/runner.rs index 290c64d62..d7692ff8b 100644 --- a/src-tauri/src/app/runner.rs +++ b/src-tauri/src/app/runner.rs @@ -30,7 +30,7 @@ pub fn run() { Ok(cfg) => cfg, Err(err) => { tracing::error!("{}", err); - eprintln!("{}", err); + eprintln!("{err}"); return; } }; @@ -40,7 +40,7 @@ pub fn run() { Ok(s) => s, Err(err) => { tracing::error!("应用状态初始化失败: {}", err); - eprintln!("应用状态初始化失败: {}", err); + eprintln!("应用状态初始化失败: {err}"); return; } }; @@ -403,7 +403,7 @@ pub fn run() { Err(e) => { tracing::error!("[Deep Link] 解析 URL 失败: {:?}", e); // 发送错误事件到前端 - let _ = app_handle_clone.emit("deep-link-error", &format!("{:?}", e)); + let _ = app_handle_clone.emit("deep-link-error", &format!("{e:?}")); } } } @@ -439,7 +439,7 @@ pub fn run() { } Err(e) => { tracing::error!("[Deep Link] 解析 URL 失败: {:?}", e); - let _ = app_handle_clone.emit("deep-link-error", &format!("{:?}", e)); + let _ = app_handle_clone.emit("deep-link-error", &format!("{e:?}")); } } } @@ -485,7 +485,7 @@ pub fn run() { "claude_oauth" => "Claude OAuth", _ => &provider_overview.provider_type, }; - loaded_types.push(format!("{} ({} 个)", provider_name, count)); + loaded_types.push(format!("{provider_name} ({count} 个)")); } } @@ -503,7 +503,7 @@ pub fn run() { Err(e) => { logs.write() .await - .add("warn", &format!("[启动] 获取凭证池信息失败: {}", e)); + .add("warn", &format!("[启动] 获取凭证池信息失败: {e}")); } } @@ -546,7 +546,7 @@ pub fn run() { .await .add("info", &format!("[启动] 服务器已启动: {host}:{port}")); server_started = true; - server_address = format!("{}:{}", host, port); + server_address = format!("{host}:{port}"); } Err(e) => { logs.write() diff --git a/src-tauri/src/app/setup.rs b/src-tauri/src/app/setup.rs index 14cee3531..db44933ff 100644 --- a/src-tauri/src/app/setup.rs +++ b/src-tauri/src/app/setup.rs @@ -141,7 +141,7 @@ async fn start_server_async( "iflow" => "iFlow", _ => &provider_overview.provider_type, }; - loaded_types.push(format!("{} ({} 个)", provider_name, count)); + loaded_types.push(format!("{provider_name} ({count} 个)")); } } @@ -159,7 +159,7 @@ async fn start_server_async( Err(e) => { logs.write() .await - .add("warn", &format!("[启动] 获取凭证池信息失败: {}", e)); + .add("warn", &format!("[启动] 获取凭证池信息失败: {e}")); } } @@ -203,7 +203,7 @@ async fn start_server_async( .await .add("info", &format!("[启动] 服务器已启动: {host}:{port}")); server_started = true; - server_address = format!("{}:{}", host, port); + server_address = format!("{host}:{port}"); } Err(e) => { logs.write() diff --git a/src-tauri/src/backends/traits.rs b/src-tauri/src/backends/traits.rs index 9ef73bf87..4f0658877 100644 --- a/src-tauri/src/backends/traits.rs +++ b/src-tauri/src/backends/traits.rs @@ -163,10 +163,10 @@ mod tests { #[test] fn test_backend_error_display() { let err = BackendError::new(BackendErrorKind::NetworkError, "connection refused"); - assert_eq!(format!("{}", err), "NetworkError: connection refused"); + assert_eq!(format!("{err}"), "NetworkError: connection refused"); let err = BackendError::with_status(BackendErrorKind::ServerError, "internal error", 500); - assert_eq!(format!("{}", err), "ServerError (500): internal error"); + assert_eq!(format!("{err}"), "ServerError (500): internal error"); } #[test] diff --git a/src-tauri/src/browser_interceptor/config.rs b/src-tauri/src/browser_interceptor/config.rs index 23ac7b0f0..cd4a9b213 100644 --- a/src-tauri/src/browser_interceptor/config.rs +++ b/src-tauri/src/browser_interceptor/config.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; /// 拦截器状态 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct InterceptorState { pub enabled: bool, pub active_hooks: Vec, @@ -11,18 +11,6 @@ pub struct InterceptorState { pub can_restore: bool, // 是否可以恢复正常状态 } -impl Default for InterceptorState { - fn default() -> Self { - Self { - enabled: false, - active_hooks: Vec::new(), - intercepted_count: 0, - last_activity: None, - can_restore: false, - } - } -} - /// 被拦截的 URL 信息 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InterceptedUrl { @@ -50,7 +38,7 @@ impl InterceptedUrl { } /// 指纹浏览器配置 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct FingerprintBrowserConfig { pub enabled: bool, pub executable_path: String, @@ -58,17 +46,6 @@ pub struct FingerprintBrowserConfig { pub additional_args: Vec, } -impl Default for FingerprintBrowserConfig { - fn default() -> Self { - Self { - enabled: false, - executable_path: String::new(), - profile_path: String::new(), - additional_args: Vec::new(), - } - } -} - /// 恢复机制配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RecoveryConfig { diff --git a/src-tauri/src/browser_interceptor/interceptor.rs b/src-tauri/src/browser_interceptor/interceptor.rs index a7da381ba..c0bca65f2 100644 --- a/src-tauri/src/browser_interceptor/interceptor.rs +++ b/src-tauri/src/browser_interceptor/interceptor.rs @@ -53,7 +53,7 @@ impl BrowserInterceptor { tracing::info!("验证配置..."); config.validate().map_err(|e| { tracing::error!("配置验证失败: {}", e); - BrowserInterceptorError::ConfigError(format!("配置验证失败: {}", e)) + BrowserInterceptorError::ConfigError(format!("配置验证失败: {e}")) })?; drop(config); // 释放读锁 @@ -226,7 +226,7 @@ impl BrowserInterceptor { // 验证新配置 new_config .validate() - .map_err(|e| BrowserInterceptorError::ConfigError(format!("配置验证失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::ConfigError(format!("配置验证失败: {e}")))?; let mut config = self.config.write().await; *config = new_config; @@ -375,16 +375,14 @@ impl BrowserInterceptor { Ok(mut clipboard) => { if let Err(e) = clipboard.set_text(text) { return Err(BrowserInterceptorError::InterceptorError(format!( - "复制到剪贴板失败: {}", - e + "复制到剪贴板失败: {e}" ))); } tracing::info!("已复制到剪贴板: {}", text); Ok(()) } Err(e) => Err(BrowserInterceptorError::InterceptorError(format!( - "创建剪贴板实例失败: {}", - e + "创建剪贴板实例失败: {e}" ))), } } @@ -446,8 +444,7 @@ impl BrowserInterceptor { Ok(()) } Err(e) => Err(BrowserInterceptorError::InterceptorError(format!( - "启动指纹浏览器失败: {}", - e + "启动指纹浏览器失败: {e}" ))), } } diff --git a/src-tauri/src/browser_interceptor/mod.rs b/src-tauri/src/browser_interceptor/mod.rs index 2bb354be0..914e650b7 100644 --- a/src-tauri/src/browser_interceptor/mod.rs +++ b/src-tauri/src/browser_interceptor/mod.rs @@ -46,14 +46,14 @@ pub enum BrowserInterceptorError { impl fmt::Display for BrowserInterceptorError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - BrowserInterceptorError::ConfigError(msg) => write!(f, "配置错误: {}", msg), - BrowserInterceptorError::InterceptorError(msg) => write!(f, "拦截器错误: {}", msg), - BrowserInterceptorError::StateError(msg) => write!(f, "状态管理错误: {}", msg), - BrowserInterceptorError::PlatformError(msg) => write!(f, "平台错误: {}", msg), - BrowserInterceptorError::NotificationError(msg) => write!(f, "通知错误: {}", msg), + BrowserInterceptorError::ConfigError(msg) => write!(f, "配置错误: {msg}"), + BrowserInterceptorError::InterceptorError(msg) => write!(f, "拦截器错误: {msg}"), + BrowserInterceptorError::StateError(msg) => write!(f, "状态管理错误: {msg}"), + BrowserInterceptorError::PlatformError(msg) => write!(f, "平台错误: {msg}"), + BrowserInterceptorError::NotificationError(msg) => write!(f, "通知错误: {msg}"), BrowserInterceptorError::AlreadyRunning => write!(f, "拦截器已在运行"), - BrowserInterceptorError::UnsupportedPlatform(msg) => write!(f, "不支持的平台: {}", msg), - BrowserInterceptorError::IoError(msg) => write!(f, "IO错误: {}", msg), + BrowserInterceptorError::UnsupportedPlatform(msg) => write!(f, "不支持的平台: {msg}"), + BrowserInterceptorError::IoError(msg) => write!(f, "IO错误: {msg}"), } } } diff --git a/src-tauri/src/browser_interceptor/platform/macos.rs b/src-tauri/src/browser_interceptor/platform/macos.rs index f1a954680..4d4a206f4 100644 --- a/src-tauri/src/browser_interceptor/platform/macos.rs +++ b/src-tauri/src/browser_interceptor/platform/macos.rs @@ -134,7 +134,7 @@ print("OK") .args(["-e", swift_code]) .output() .map_err(|e| { - BrowserInterceptorError::PlatformError(format!("执行 Swift 脚本失败: {}", e)) + BrowserInterceptorError::PlatformError(format!("执行 Swift 脚本失败: {e}")) })?; if output.status.success() { @@ -176,12 +176,11 @@ print("OK") import Foundation import CoreServices -let bundleId = "{}" as CFString +let bundleId = "{browser_id}" as CFString LSSetDefaultHandlerForURLScheme("http" as CFString, bundleId) LSSetDefaultHandlerForURLScheme("https" as CFString, bundleId) print("OK") -"#, - browser_id +"# ); let output = Command::new("swift").args(["-e", &swift_code]).output(); diff --git a/src-tauri/src/browser_interceptor/state_manager.rs b/src-tauri/src/browser_interceptor/state_manager.rs index 411832cf9..7935e1a5e 100644 --- a/src-tauri/src/browser_interceptor/state_manager.rs +++ b/src-tauri/src/browser_interceptor/state_manager.rs @@ -32,7 +32,7 @@ impl StateManager { pub fn get_state(&self) -> Result { self.state .read() - .map_err(|e| BrowserInterceptorError::StateError(format!("读取状态失败: {}", e))) + .map_err(|e| BrowserInterceptorError::StateError(format!("读取状态失败: {e}"))) .map(|state| state.clone()) } @@ -46,7 +46,7 @@ impl StateManager { let mut state = self .state .write() - .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {e}")))?; state.enabled = true; state.can_restore = true; @@ -63,7 +63,7 @@ impl StateManager { let mut state = self .state .write() - .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {e}")))?; state.enabled = false; state.active_hooks.clear(); @@ -80,9 +80,10 @@ impl StateManager { // 设置定时器 { - let mut timer = self.temporary_disable_timer.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("设置定时器失败: {}", e)) - })?; + let mut timer = self + .temporary_disable_timer + .write() + .map_err(|e| BrowserInterceptorError::StateError(format!("设置定时器失败: {e}")))?; *timer = Some(Instant::now() + Duration::from_secs(duration_seconds)); } @@ -113,7 +114,7 @@ impl StateManager { let mut state = self .state .write() - .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {e}")))?; state.can_restore = false; } @@ -126,7 +127,7 @@ impl StateManager { let mut state = self .state .write() - .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {e}")))?; state.intercepted_count += 1; state.last_activity = Some(Utc::now()); @@ -139,7 +140,7 @@ impl StateManager { let mut state = self .state .write() - .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {e}")))?; if !state.active_hooks.contains(&hook_name) { state.active_hooks.push(hook_name); @@ -153,7 +154,7 @@ impl StateManager { let mut state = self .state .write() - .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入状态失败: {e}")))?; state.active_hooks.retain(|h| h != hook_name); @@ -171,7 +172,7 @@ impl StateManager { { let mut backup = self.original_system_state.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("备份系统状态失败: {}", e)) + BrowserInterceptorError::StateError(format!("备份系统状态失败: {e}")) })?; *backup = Some(system_state); } @@ -184,7 +185,7 @@ impl StateManager { async fn restore_system_state(&self) -> Result<()> { let backup = { let backup_guard = self.original_system_state.read().map_err(|e| { - BrowserInterceptorError::StateError(format!("读取备份状态失败: {}", e)) + BrowserInterceptorError::StateError(format!("读取备份状态失败: {e}")) })?; backup_guard.clone() }; diff --git a/src-tauri/src/browser_interceptor/url_manager.rs b/src-tauri/src/browser_interceptor/url_manager.rs index 2bb89e12b..c4ba9d7a6 100644 --- a/src-tauri/src/browser_interceptor/url_manager.rs +++ b/src-tauri/src/browser_interceptor/url_manager.rs @@ -50,7 +50,7 @@ impl UrlManager { // 添加到当前拦截列表 { let mut urls = self.intercepted_urls.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("添加拦截 URL 失败: {}", e)) + BrowserInterceptorError::StateError(format!("添加拦截 URL 失败: {e}")) })?; urls.insert(id.clone(), intercepted_url.clone()); } @@ -58,7 +58,7 @@ impl UrlManager { // 添加到历史记录 { let mut history = self.history.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("添加历史记录失败: {}", e)) + BrowserInterceptorError::StateError(format!("添加历史记录失败: {e}")) })?; history.push(intercepted_url); @@ -82,9 +82,10 @@ impl UrlManager { /// 获取所有当前拦截的 URL pub fn get_intercepted_urls(&self) -> Result> { - let urls = self.intercepted_urls.read().map_err(|e| { - BrowserInterceptorError::StateError(format!("读取拦截 URL 失败: {}", e)) - })?; + let urls = self + .intercepted_urls + .read() + .map_err(|e| BrowserInterceptorError::StateError(format!("读取拦截 URL 失败: {e}")))?; let mut result: Vec = urls.values().cloned().collect(); result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); // 按时间倒序排列 @@ -94,18 +95,20 @@ impl UrlManager { /// 获取指定 ID 的拦截 URL pub fn get_intercepted_url(&self, id: &str) -> Result> { - let urls = self.intercepted_urls.read().map_err(|e| { - BrowserInterceptorError::StateError(format!("读取拦截 URL 失败: {}", e)) - })?; + let urls = self + .intercepted_urls + .read() + .map_err(|e| BrowserInterceptorError::StateError(format!("读取拦截 URL 失败: {e}")))?; Ok(urls.get(id).cloned()) } /// 标记 URL 为已复制 pub fn mark_as_copied(&self, id: &str) -> Result<()> { - let mut urls = self.intercepted_urls.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("更新拦截 URL 失败: {}", e)) - })?; + let mut urls = self + .intercepted_urls + .write() + .map_err(|e| BrowserInterceptorError::StateError(format!("更新拦截 URL 失败: {e}")))?; if let Some(url) = urls.get_mut(id) { url.copied = true; @@ -117,9 +120,10 @@ impl UrlManager { /// 标记 URL 为已在浏览器中打开 pub fn mark_as_opened(&self, id: &str) -> Result<()> { - let mut urls = self.intercepted_urls.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("更新拦截 URL 失败: {}", e)) - })?; + let mut urls = self + .intercepted_urls + .write() + .map_err(|e| BrowserInterceptorError::StateError(format!("更新拦截 URL 失败: {e}")))?; if let Some(url) = urls.get_mut(id) { url.opened_in_browser = true; @@ -131,16 +135,17 @@ impl UrlManager { /// 忽略(移除)指定的 URL pub fn dismiss_url(&self, id: &str) -> Result<()> { - let mut urls = self.intercepted_urls.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("移除拦截 URL 失败: {}", e)) - })?; + let mut urls = self + .intercepted_urls + .write() + .map_err(|e| BrowserInterceptorError::StateError(format!("移除拦截 URL 失败: {e}")))?; if let Some(mut url) = urls.remove(id) { url.dismissed = true; // 更新历史记录中的状态 let mut history = self.history.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("更新历史记录失败: {}", e)) + BrowserInterceptorError::StateError(format!("更新历史记录失败: {e}")) })?; if let Some(history_url) = history.iter_mut().find(|u| u.id == id) { @@ -155,9 +160,10 @@ impl UrlManager { /// 清除所有当前拦截的 URL pub fn clear_intercepted_urls(&self) -> Result<()> { - let mut urls = self.intercepted_urls.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("清除拦截 URL 失败: {}", e)) - })?; + let mut urls = self + .intercepted_urls + .write() + .map_err(|e| BrowserInterceptorError::StateError(format!("清除拦截 URL 失败: {e}")))?; let count = urls.len(); urls.clear(); @@ -171,7 +177,7 @@ impl UrlManager { let history = self .history .read() - .map_err(|e| BrowserInterceptorError::StateError(format!("读取历史记录失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("读取历史记录失败: {e}")))?; let mut result = history.clone(); result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); // 按时间倒序排列 @@ -188,7 +194,7 @@ impl UrlManager { let history = self .history .read() - .map_err(|e| BrowserInterceptorError::StateError(format!("搜索历史记录失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("搜索历史记录失败: {e}")))?; let query_lower = query.to_lowercase(); let mut result: Vec = history @@ -211,14 +217,15 @@ impl UrlManager { /// 获取统计信息 pub fn get_statistics(&self) -> Result { - let urls = self.intercepted_urls.read().map_err(|e| { - BrowserInterceptorError::StateError(format!("读取拦截 URL 失败: {}", e)) - })?; + let urls = self + .intercepted_urls + .read() + .map_err(|e| BrowserInterceptorError::StateError(format!("读取拦截 URL 失败: {e}")))?; let history = self .history .read() - .map_err(|e| BrowserInterceptorError::StateError(format!("读取历史记录失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("读取历史记录失败: {e}")))?; let current_count = urls.len(); let total_intercepted = history.len(); @@ -246,7 +253,7 @@ impl UrlManager { pub fn save_to_storage(&self) -> Result<()> { if let Some(storage_path) = &self.storage_path { let history = self.history.read().map_err(|e| { - BrowserInterceptorError::StateError(format!("读取历史记录失败: {}", e)) + BrowserInterceptorError::StateError(format!("读取历史记录失败: {e}")) })?; let storage_data = UrlStorageData { @@ -255,19 +262,18 @@ impl UrlManager { saved_at: Utc::now(), }; - let json_data = serde_json::to_string_pretty(&storage_data).map_err(|e| { - BrowserInterceptorError::StateError(format!("序列化数据失败: {}", e)) - })?; + let json_data = serde_json::to_string_pretty(&storage_data) + .map_err(|e| BrowserInterceptorError::StateError(format!("序列化数据失败: {e}")))?; // 确保目录存在 if let Some(parent) = Path::new(storage_path).parent() { fs::create_dir_all(parent).map_err(|e| { - BrowserInterceptorError::StateError(format!("创建目录失败: {}", e)) + BrowserInterceptorError::StateError(format!("创建目录失败: {e}")) })?; } fs::write(storage_path, json_data) - .map_err(|e| BrowserInterceptorError::StateError(format!("写入文件失败: {}", e)))?; + .map_err(|e| BrowserInterceptorError::StateError(format!("写入文件失败: {e}")))?; tracing::info!("已保存历史记录到: {}", storage_path); } @@ -280,17 +286,17 @@ impl UrlManager { if let Some(storage_path) = &self.storage_path { if Path::new(storage_path).exists() { let json_data = fs::read_to_string(storage_path).map_err(|e| { - BrowserInterceptorError::StateError(format!("读取文件失败: {}", e)) + BrowserInterceptorError::StateError(format!("读取文件失败: {e}")) })?; let storage_data: UrlStorageData = serde_json::from_str(&json_data).map_err(|e| { - BrowserInterceptorError::StateError(format!("反序列化数据失败: {}", e)) + BrowserInterceptorError::StateError(format!("反序列化数据失败: {e}")) })?; { let mut history = self.history.write().map_err(|e| { - BrowserInterceptorError::StateError(format!("写入历史记录失败: {}", e)) + BrowserInterceptorError::StateError(format!("写入历史记录失败: {e}")) })?; *history = storage_data.history; } diff --git a/src-tauri/src/commands/agent_cmd.rs b/src-tauri/src/commands/agent_cmd.rs index 7879cb78d..f575d6728 100644 --- a/src-tauri/src/commands/agent_cmd.rs +++ b/src-tauri/src/commands/agent_cmd.rs @@ -53,7 +53,7 @@ pub async fn agent_start_process( agent_state.init_agent_with_db(&db).await?; - let base_url = format!("http://{}:{}", host, port); + let base_url = format!("http://{host}:{port}"); Ok(AgentProcessStatus { running: true, @@ -153,7 +153,7 @@ pub async fn agent_create_session( }; { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; if let Err(e) = AgentDao::create_session(&conn, &session) { tracing::warn!("[Agent] 保存会话到数据库失败: {}", e); } @@ -180,10 +180,10 @@ fn build_system_prompt_with_skills( xml.push_str(" \n"); xml.push_str(&format!(" {}\n", skill.name)); if let Some(desc) = &skill.description { - xml.push_str(&format!(" {}\n", desc)); + xml.push_str(&format!(" {desc}\n")); } if let Some(path) = &skill.path { - xml.push_str(&format!(" {}\n", path)); + xml.push_str(&format!(" {path}\n")); } xml.push_str(" \n"); } @@ -196,7 +196,7 @@ fn build_system_prompt_with_skills( }; match (base_prompt, skills_xml) { - (Some(base), Some(skills)) => Some(format!("{}\n\n{}", base, skills)), + (Some(base), Some(skills)) => Some(format!("{base}\n\n{skills}")), (Some(base), None) => Some(base), (None, Some(skills)) => Some(skills), (None, None) => None, @@ -242,10 +242,9 @@ pub struct SessionInfo { /// 获取会话列表 #[tauri::command] pub async fn agent_list_sessions(db: State<'_, DbConnection>) -> Result, String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; - let sessions = - AgentDao::list_sessions(&conn).map_err(|e| format!("获取会话列表失败: {}", e))?; + let sessions = AgentDao::list_sessions(&conn).map_err(|e| format!("获取会话列表失败: {e}"))?; let result: Vec = sessions .into_iter() @@ -272,10 +271,10 @@ pub async fn agent_get_session( db: State<'_, DbConnection>, session_id: String, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let session = AgentDao::get_session(&conn, &session_id) - .map_err(|e| format!("获取会话失败: {}", e))? + .map_err(|e| format!("获取会话失败: {e}"))? .ok_or_else(|| "会话不存在".to_string())?; let messages_count = AgentDao::get_message_count(&conn, &session_id).unwrap_or(0); @@ -297,8 +296,8 @@ pub async fn agent_delete_session( db: State<'_, DbConnection>, session_id: String, ) -> Result<(), String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; - AgentDao::delete_session(&conn, &session_id).map_err(|e| format!("删除会话失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; + AgentDao::delete_session(&conn, &session_id).map_err(|e| format!("删除会话失败: {e}"))?; Ok(()) } @@ -308,9 +307,9 @@ pub async fn agent_get_session_messages( db: State<'_, DbConnection>, session_id: String, ) -> Result, String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let messages = - AgentDao::get_messages(&conn, &session_id).map_err(|e| format!("获取消息失败: {}", e))?; + AgentDao::get_messages(&conn, &session_id).map_err(|e| format!("获取消息失败: {e}"))?; Ok(messages) } @@ -321,9 +320,9 @@ pub async fn agent_rename_session( session_id: String, title: String, ) -> Result<(), String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; AgentDao::update_title(&conn, &session_id, &title) - .map_err(|e| format!("更新会话标题失败: {}", e))?; + .map_err(|e| format!("更新会话标题失败: {e}"))?; Ok(()) } @@ -335,11 +334,11 @@ pub async fn agent_generate_title( db: State<'_, DbConnection>, session_id: String, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; // 获取会话的前几条消息(用于生成标题) let messages = - AgentDao::get_messages(&conn, &session_id).map_err(|e| format!("获取消息失败: {}", e))?; + AgentDao::get_messages(&conn, &session_id).map_err(|e| format!("获取消息失败: {e}"))?; // 过滤出 user 和 assistant 消息 let chat_messages: Vec<_> = messages @@ -366,7 +365,7 @@ pub async fn agent_generate_title( } else { content }; - conversation.push_str(&format!("{}:{}\n", role, truncated_content)); + conversation.push_str(&format!("{role}:{truncated_content}\n")); } // 使用 AI 生成标题(通过 aster_agent_chat_stream 生成) diff --git a/src-tauri/src/commands/api_key_provider_cmd.rs b/src-tauri/src/commands/api_key_provider_cmd.rs index c9b1c26b2..7e90790d5 100644 --- a/src-tauri/src/commands/api_key_provider_cmd.rs +++ b/src-tauri/src/commands/api_key_provider_cmd.rs @@ -120,7 +120,7 @@ fn mask_api_key(key: &str) -> String { } else { let prefix: String = chars[..6].iter().collect(); let suffix: String = chars[chars.len() - 4..].iter().collect(); - format!("{}****{}", prefix, suffix) + format!("{prefix}****{suffix}") } } @@ -222,7 +222,7 @@ pub fn add_custom_api_key_provider( let provider_type: ApiProviderType = request .provider_type .parse() - .map_err(|e: String| format!("无效的 Provider 类型: {}", e))?; + .map_err(|e: String| format!("无效的 Provider 类型: {e}"))?; let provider = service.0.add_custom_provider( &db, @@ -251,7 +251,7 @@ pub fn update_api_key_provider( .provider_type .map(|t| t.parse()) .transpose() - .map_err(|e: String| format!("无效的 Provider 类型: {}", e))?; + .map_err(|e: String| format!("无效的 Provider 类型: {e}"))?; let provider = service.0.update_provider( &db, @@ -403,7 +403,7 @@ pub fn export_api_key_providers( include_keys: bool, ) -> Result { let config = service.0.export_config(&db, include_keys)?; - serde_json::to_string_pretty(&config).map_err(|e| format!("序列化失败: {}", e)) + serde_json::to_string_pretty(&config).map_err(|e| format!("序列化失败: {e}")) } /// 导入 Provider 配置 diff --git a/src-tauri/src/commands/asr_cmd.rs b/src-tauri/src/commands/asr_cmd.rs index 1a771cf99..fd2ab7d1c 100644 --- a/src-tauri/src/commands/asr_cmd.rs +++ b/src-tauri/src/commands/asr_cmd.rs @@ -120,7 +120,7 @@ pub async fn delete_asr_credential(id: String) -> Result<(), String> { .asr .iter() .position(|c| c.id == id) - .ok_or_else(|| format!("凭证不存在: {}", id))?; + .ok_or_else(|| format!("凭证不存在: {id}"))?; let was_default = config.credential_pool.asr[idx].is_default; config.credential_pool.asr.remove(idx); @@ -143,7 +143,7 @@ pub async fn set_default_asr_credential(id: String) -> Result<(), String> { // 检查凭证是否存在 let exists = config.credential_pool.asr.iter().any(|c| c.id == id); if !exists { - return Err(format!("凭证不存在: {}", id)); + return Err(format!("凭证不存在: {id}")); } // 更新默认状态 @@ -166,7 +166,7 @@ pub async fn test_asr_credential(id: String) -> Result { .asr .iter() .find(|c| c.id == id) - .ok_or_else(|| format!("凭证不存在: {}", id))?; + .ok_or_else(|| format!("凭证不存在: {id}"))?; // 根据 Provider 类型测试 match credential.provider { diff --git a/src-tauri/src/commands/aster_agent_cmd.rs b/src-tauri/src/commands/aster_agent_cmd.rs index e2365b2fe..094d10ead 100644 --- a/src-tauri/src/commands/aster_agent_cmd.rs +++ b/src-tauri/src/commands/aster_agent_cmd.rs @@ -12,42 +12,10 @@ use crate::agent::{ use crate::database::dao::agent::AgentDao; use crate::database::DbConnection; use aster::conversation::message::Message; -use aster::session::SessionManager; use futures::StreamExt; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; use tauri::{AppHandle, Emitter, State}; -/// 确保 session 在 Aster 数据库中存在 -/// 如果不存在则创建新的 session -async fn ensure_session_exists(session_id: &str) -> Result { - // 尝试获取现有 session - match SessionManager::get_session(session_id, false).await { - Ok(_) => { - tracing::debug!("[AsterAgent] Session 已存在: {}", session_id); - Ok(session_id.to_string()) - } - Err(_) => { - // Session 不存在,创建新的 - tracing::info!( - "[AsterAgent] Session 不存在,创建新 session: {}", - session_id - ); - let working_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); - let session = SessionManager::create_session( - working_dir, - "New Chat".to_string(), - aster::session::SessionType::User, - ) - .await - .map_err(|e| format!("创建 session 失败: {}", e))?; - - tracing::info!("[AsterAgent] 创建新 session: {}", session.id); - Ok(session.id) - } - } -} - /// Aster Agent 状态信息 #[derive(Debug, Serialize)] pub struct AsterAgentStatus { @@ -224,23 +192,54 @@ pub async fn aster_agent_chat_stream( ); // 确保 Agent 已初始化(使用带数据库的版本,注入 SessionStore) - if !state.is_initialized().await { + let is_init = state.is_initialized().await; + tracing::warn!("[AsterAgent] Agent 初始化状态: {}", is_init); + if !is_init { + tracing::warn!("[AsterAgent] Agent 未初始化,开始初始化..."); state.init_agent_with_db(&db).await?; + tracing::warn!("[AsterAgent] Agent 初始化完成"); + } else { + tracing::warn!("[AsterAgent] Agent 已初始化,检查 session_store..."); + // 检查 session_store 是否存在 + let agent_arc = state.get_agent_arc(); + let guard = agent_arc.read().await; + if let Some(agent) = guard.as_ref() { + let has_store = agent.session_store().is_some(); + tracing::warn!("[AsterAgent] session_store 存在: {}", has_store); + } } - // 确保 session 在数据库中存在 - // 如果 session 不存在,自动创建 - let session_id = ensure_session_exists(&request.session_id).await?; + // 直接使用前端传递的 session_id + // ProxyCastSessionStore 会在 add_message 时自动创建不存在的 session + // 同时 get_session 也会自动创建不存在的 session + let session_id = &request.session_id; - // 从数据库读取 session 的 system_prompt + // 从 ProxyCast 数据库读取 session 的 system_prompt(如果存在) let system_prompt = { - let db_conn = db - .lock() - .map_err(|e| format!("获取数据库连接失败: {}", e))?; - let session = AgentDao::get_session(&db_conn, &session_id) - .map_err(|e| format!("获取 session 失败: {}", e))? - .ok_or_else(|| format!("Session 不存在: {}", session_id))?; - session.system_prompt + let db_conn = db.lock().map_err(|e| format!("获取数据库连接失败: {e}"))?; + match AgentDao::get_session(&db_conn, session_id) { + Ok(Some(session)) => { + tracing::debug!( + "[AsterAgent] 找到 session,system_prompt: {:?}", + session.system_prompt.as_ref().map(|s| s.len()) + ); + session.system_prompt + } + Ok(None) => { + tracing::debug!( + "[AsterAgent] ProxyCast 数据库中未找到 session: {}", + session_id + ); + None + } + Err(e) => { + tracing::warn!( + "[AsterAgent] 读取 session 失败: {}, 继续使用空 system_prompt", + e + ); + None + } + } }; // 如果提供了 Provider 配置,则配置 Provider @@ -252,7 +251,7 @@ pub async fn aster_agent_chat_stream( base_url: provider_config.base_url.clone(), credential_uuid: None, }; - state.configure_provider(config, &session_id, &db).await?; + state.configure_provider(config, session_id, &db).await?; } // 检查 Provider 是否已配置 @@ -261,13 +260,13 @@ pub async fn aster_agent_chat_stream( } // 创建取消令牌 - let cancel_token = state.create_cancel_token(&session_id).await; + let cancel_token = state.create_cancel_token(session_id).await; // 创建用户消息 let user_message = Message::user().with_text(&request.message); // 创建会话配置,包含 system_prompt - let mut session_config_builder = SessionConfigBuilder::new(&session_id); + let mut session_config_builder = SessionConfigBuilder::new(session_id); if let Some(prompt) = system_prompt { session_config_builder = session_config_builder.system_prompt(prompt); } @@ -302,7 +301,7 @@ pub async fn aster_agent_chat_stream( Err(e) => { // 发送错误事件 let error_event = TauriAgentEvent::Error { - message: format!("Stream error: {}", e), + message: format!("Stream error: {e}"), }; if let Err(emit_err) = app.emit(&request.event_name, &error_event) { tracing::error!("[AsterAgent] 发送错误事件失败: {}", emit_err); @@ -320,19 +319,19 @@ pub async fn aster_agent_chat_stream( Err(e) => { // 发送错误事件 let error_event = TauriAgentEvent::Error { - message: format!("Agent error: {}", e), + message: format!("Agent error: {e}"), }; if let Err(emit_err) = app.emit(&request.event_name, &error_event) { tracing::error!("[AsterAgent] 发送错误事件失败: {}", emit_err); } - return Err(format!("Agent error: {}", e)); + return Err(format!("Agent error: {e}")); } } // guard 会在函数结束时自动释放(stream_result 先释放) // 清理取消令牌 - state.remove_cancel_token(&session_id).await; + state.remove_cancel_token(session_id).await; Ok(()) } @@ -350,26 +349,28 @@ pub async fn aster_agent_stop( /// 创建新会话 #[tauri::command] pub async fn aster_session_create( - working_dir: Option, + db: State<'_, DbConnection>, name: Option, ) -> Result { tracing::info!("[AsterAgent] 创建会话: name={:?}", name); - let dir = working_dir.map(PathBuf::from); - AsterAgentWrapper::create_session(dir, name).await + AsterAgentWrapper::create_session_sync(&db, name) } /// 列出所有会话 #[tauri::command] -pub async fn aster_session_list() -> Result, String> { +pub async fn aster_session_list(db: State<'_, DbConnection>) -> Result, String> { tracing::info!("[AsterAgent] 列出会话"); - AsterAgentWrapper::list_sessions().await + AsterAgentWrapper::list_sessions_sync(&db) } /// 获取会话详情 #[tauri::command] -pub async fn aster_session_get(session_id: String) -> Result { +pub async fn aster_session_get( + db: State<'_, DbConnection>, + session_id: String, +) -> Result { tracing::info!("[AsterAgent] 获取会话: {}", session_id); - AsterAgentWrapper::get_session(&session_id).await + AsterAgentWrapper::get_session_sync(&db, &session_id) } /// 确认权限请求 diff --git a/src-tauri/src/commands/auto_fix_cmd.rs b/src-tauri/src/commands/auto_fix_cmd.rs index 9d1f3733b..774fe1300 100644 --- a/src-tauri/src/commands/auto_fix_cmd.rs +++ b/src-tauri/src/commands/auto_fix_cmd.rs @@ -36,14 +36,12 @@ pub async fn auto_fix_configuration( // 检查默认Provider配置 if let Err(e) = fix_default_provider_issue(&state, &logs, &db, &mut result).await { - result - .warnings - .push(format!("修复默认Provider时出错: {}", e)); + result.warnings.push(format!("修复默认Provider时出错: {e}")); } // 检查凭证池状态 if let Err(e) = check_credential_pool_issues(&db, &mut result).await { - result.warnings.push(format!("检查凭证池时出错: {}", e)); + result.warnings.push(format!("检查凭证池时出错: {e}")); } logs.write().await.add( @@ -84,7 +82,7 @@ async fn fix_default_provider_issue( if let Err(e) = set_default_provider_internal(state, logs, "kiro".to_string()).await { result .warnings - .push(format!("无法自动修复默认Provider: {}", e)); + .push(format!("无法自动修复默认Provider: {e}")); } else { result .fixes_applied @@ -98,7 +96,7 @@ async fn fix_default_provider_issue( else if !is_provider_available(¤t_default, &credential_stats) { result .issues_found - .push(format!("默认Provider '{}' 没有可用凭证", current_default)); + .push(format!("默认Provider '{current_default}' 没有可用凭证")); // 寻找最佳替代Provider if let Some(best_provider) = find_best_available_provider(&credential_stats) { @@ -106,14 +104,14 @@ async fn fix_default_provider_issue( { result .warnings - .push(format!("无法自动修复默认Provider: {}", e)); + .push(format!("无法自动修复默认Provider: {e}")); } else { result .fixes_applied - .push(format!("默认Provider已自动设置为 '{}'", best_provider)); + .push(format!("默认Provider已自动设置为 '{best_provider}'")); logs.write().await.add( "info", - &format!("[自动修复] 默认Provider已设置为{}", best_provider), + &format!("[自动修复] 默认Provider已设置为{best_provider}"), ); } } else { @@ -249,7 +247,7 @@ async fn check_credential_pool_issues( if expired_tokens > 0 { result .issues_found - .push(format!("发现 {} 个过期的token缓存", expired_tokens)); + .push(format!("发现 {expired_tokens} 个过期的token缓存")); // 过期token会在使用时自动刷新,这里只是报告 } @@ -258,7 +256,7 @@ async fn check_credential_pool_issues( if disabled_count > 0 { result .issues_found - .push(format!("有 {} 个凭证被禁用", disabled_count)); + .push(format!("有 {disabled_count} 个凭证被禁用")); } Ok(()) diff --git a/src-tauri/src/commands/browser_interceptor_cmd.rs b/src-tauri/src/commands/browser_interceptor_cmd.rs index 67c8c997b..78a14cb7e 100644 --- a/src-tauri/src/commands/browser_interceptor_cmd.rs +++ b/src-tauri/src/commands/browser_interceptor_cmd.rs @@ -78,7 +78,7 @@ pub async fn temporary_disable_interceptor(duration_seconds: u64) -> Result Result Result { - config.validate().map_err(|e| e)?; + config.validate()?; Ok("配置验证通过".to_string()) } diff --git a/src-tauri/src/commands/config_cmd.rs b/src-tauri/src/commands/config_cmd.rs index 8d1fec61b..90beb5165 100644 --- a/src-tauri/src/commands/config_cmd.rs +++ b/src-tauri/src/commands/config_cmd.rs @@ -242,7 +242,7 @@ pub fn export_config(config: Config, redact_secrets: bool) -> Result "credentials", (false, false) => "empty", }; - let suggested_filename = format!("proxycast_{}_{}{}.json", scope, timestamp, suffix); + let suggested_filename = format!("proxycast_{scope}_{timestamp}{suffix}.json"); Ok(UnifiedExportResult { content, @@ -460,7 +460,7 @@ pub fn export_config_yaml(config: Config, redact_secrets: bool) -> Result Result { latest: None, has_update: false, download_url: None, - error: Some(format!("解析响应失败: {}", e)), + error: Some(format!("解析响应失败: {e}")), }), } } else { @@ -657,7 +657,7 @@ pub async fn check_for_updates() -> Result { latest: None, has_update: false, download_url: None, - error: Some(format!("网络请求失败: {}", e)), + error: Some(format!("网络请求失败: {e}")), }), } } @@ -837,27 +837,27 @@ pub async fn download_update(app_handle: AppHandle) -> Result Ok(DownloadResult { success: false, - message: format!("保存文件失败: {}", e), + message: format!("保存文件失败: {e}"), file_path: None, }), } } Err(e) => Ok(DownloadResult { success: false, - message: format!("读取下载内容失败: {}", e), + message: format!("读取下载内容失败: {e}"), file_path: None, }), } } Err(e) => Ok(DownloadResult { success: false, - message: format!("网络请求失败: {}", e), + message: format!("网络请求失败: {e}"), file_path: None, }), } @@ -865,10 +865,8 @@ pub async fn download_update(app_handle: AppHandle) -> Result Result<(String, String), String> { - let api_url = format!( - "https://api.github.com/repos/aiclientproxy/proxycast/releases/tags/v{}", - version - ); + let api_url = + format!("https://api.github.com/repos/aiclientproxy/proxycast/releases/tags/v{version}"); let client = reqwest::Client::new(); let response = client @@ -876,7 +874,7 @@ async fn get_platform_download_from_github(version: &str) -> Result<(String, Str .header("User-Agent", "ProxyCast") .send() .await - .map_err(|e| format!("请求 GitHub API 失败: {}", e))?; + .map_err(|e| format!("请求 GitHub API 失败: {e}"))?; if !response.status().is_success() { return Err(format!("GitHub API 请求失败: {}", response.status())); @@ -885,7 +883,7 @@ async fn get_platform_download_from_github(version: &str) -> Result<(String, Str let data: serde_json::Value = response .json() .await - .map_err(|e| format!("解析 GitHub API 响应失败: {}", e))?; + .map_err(|e| format!("解析 GitHub API 响应失败: {e}"))?; let assets = data["assets"] .as_array() @@ -970,12 +968,12 @@ fn get_download_directory(app_handle: &AppHandle) -> Result { let app_data_dir = app_handle .path() .app_data_dir() - .map_err(|e| format!("无法获取应用数据目录: {}", e))?; + .map_err(|e| format!("无法获取应用数据目录: {e}"))?; let download_dir = app_data_dir.join("downloads"); // 确保目录存在 - std::fs::create_dir_all(&download_dir).map_err(|e| format!("创建下载目录失败: {}", e))?; + std::fs::create_dir_all(&download_dir).map_err(|e| format!("创建下载目录失败: {e}"))?; Ok(download_dir) } @@ -1007,9 +1005,9 @@ fn run_installer(file_path: &PathBuf) -> Result<(), String> { { tracing::info!("macOS: 打开 DMG 文件: {:?}", file_path); std::process::Command::new("open") - .arg(&file_path) + .arg(file_path) .spawn() - .map_err(|e| format!("打开 macOS DMG 文件失败: {}", e))?; + .map_err(|e| format!("打开 macOS DMG 文件失败: {e}"))?; } #[cfg(not(target_os = "macos"))] @@ -1060,7 +1058,7 @@ fn run_installer(file_path: &PathBuf) -> Result<(), String> { } } _ => { - return Err(format!("不支持的文件类型: {}", extension)); + return Err(format!("不支持的文件类型: {extension}")); } } @@ -1083,9 +1081,9 @@ fn open_file_location(file_path: &PathBuf) -> Result<(), String> { { tracing::info!("macOS: 使用 open -R 打开文件位置: {:?}", file_path); std::process::Command::new("open") - .args(&["-R", &file_path.to_string_lossy()]) + .args(["-R", &file_path.to_string_lossy()]) .spawn() - .map_err(|e| format!("macOS open 命令失败: {}", e))?; + .map_err(|e| format!("macOS open 命令失败: {e}"))?; } #[cfg(target_os = "linux")] diff --git a/src-tauri/src/commands/connect_cmd.rs b/src-tauri/src/commands/connect_cmd.rs index e52c62d8d..67a35e239 100644 --- a/src-tauri/src/commands/connect_cmd.rs +++ b/src-tauri/src/commands/connect_cmd.rs @@ -96,7 +96,7 @@ pub async fn init_connect_state(app_data_dir: PathBuf) -> Result ConnectionResponse { // 检查连接是否存在 if !config.connections.contains_key(&name) { - return ConnectionResponse::err(format!("连接 '{}' 不存在", name)); + return ConnectionResponse::err(format!("连接 '{name}' 不存在")); } // 删除并保存 @@ -234,7 +234,7 @@ pub async fn connection_test(name: String) -> ConnectionResponse { // 获取连接 let conn = match config.get(&name) { Some(c) => c, - None => return ConnectionResponse::err(format!("连接 '{}' 不存在", name)), + None => return ConnectionResponse::err(format!("连接 '{name}' 不存在")), }; // 根据连接类型测试 @@ -253,14 +253,14 @@ pub async fn connection_test(name: String) -> ConnectionResponse { let port = conn.port.unwrap_or(22); // 简单的 TCP 连接测试 - match tokio::net::TcpStream::connect(format!("{}:{}", host, port)).await { + match tokio::net::TcpStream::connect(format!("{host}:{port}")).await { Ok(_) => { tracing::info!("[Connection] SSH 连接测试成功: {}:{}", host, port); ConnectionResponse::ok() } Err(e) => { tracing::warn!("[Connection] SSH 连接测试失败: {} - {}", host, e); - ConnectionResponse::err(format!("连接失败: {}", e)) + ConnectionResponse::err(format!("连接失败: {e}")) } } } @@ -303,7 +303,7 @@ pub fn connection_import_ssh_host(host_name: String) -> ConnectionResponse { // 查找指定的 host let ssh_host = match ssh_hosts.into_iter().find(|h| h.pattern == host_name) { Some(h) => h, - None => return ConnectionResponse::err(format!("SSH Host '{}' 不存在", host_name)), + None => return ConnectionResponse::err(format!("SSH Host '{host_name}' 不存在")), }; // 加载用户配置 @@ -314,7 +314,7 @@ pub fn connection_import_ssh_host(host_name: String) -> ConnectionResponse { // 检查是否已存在 if config.connections.contains_key(&host_name) { - return ConnectionResponse::err(format!("连接 '{}' 已存在", host_name)); + return ConnectionResponse::err(format!("连接 '{host_name}' 已存在")); } // 创建连接配置 diff --git a/src-tauri/src/commands/flow_monitor_cmd.rs b/src-tauri/src/commands/flow_monitor_cmd.rs index 9bf46c413..9a8cb8029 100644 --- a/src-tauri/src/commands/flow_monitor_cmd.rs +++ b/src-tauri/src/commands/flow_monitor_cmd.rs @@ -206,7 +206,7 @@ pub async fn query_flows( request.page_size, ) .await - .map_err(|e| format!("查询 Flow 失败: {}", e)) + .map_err(|e| format!("查询 Flow 失败: {e}")) } /// 获取单个 Flow 详情 @@ -230,7 +230,7 @@ pub async fn get_flow_detail( .0 .get_flow(&flow_id) .await - .map_err(|e| format!("获取 Flow 详情失败: {}", e)) + .map_err(|e| format!("获取 Flow 详情失败: {e}")) } /// 全文搜索 Flow @@ -253,7 +253,7 @@ pub async fn search_flows( .0 .search(&request.query, request.limit) .await - .map_err(|e| format!("搜索 Flow 失败: {}", e)) + .map_err(|e| format!("搜索 Flow 失败: {e}")) } /// 获取 Flow 统计信息 @@ -309,7 +309,7 @@ pub async fn export_flows( .0 .query(filter, FlowSortBy::CreatedAt, true, 1, 10000) .await - .map_err(|e| format!("查询 Flow 失败: {}", e))?; + .map_err(|e| format!("查询 Flow 失败: {e}"))?; result.flows }; @@ -331,11 +331,11 @@ pub async fn export_flows( let data = match request.format { ExportFormat::HAR => { let har = exporter.export_har(&flows); - serde_json::to_string_pretty(&har).map_err(|e| format!("序列化 HAR 失败: {}", e))? + serde_json::to_string_pretty(&har).map_err(|e| format!("序列化 HAR 失败: {e}"))? } ExportFormat::JSON => { let json = exporter.export_json(&flows); - serde_json::to_string_pretty(&json).map_err(|e| format!("序列化 JSON 失败: {}", e))? + serde_json::to_string_pretty(&json).map_err(|e| format!("序列化 JSON 失败: {e}"))? } ExportFormat::JSONL => exporter.export_jsonl(&flows), ExportFormat::Markdown => exporter.export_markdown_multiple(&flows), @@ -516,7 +516,7 @@ pub async fn cleanup_flows( } Err(e) => { tracing::error!("清理所有数据失败: {}", e); - return Err(format!("清理所有数据失败: {}", e)); + return Err(format!("清理所有数据失败: {e}")); } } } @@ -550,7 +550,7 @@ pub async fn cleanup_flows( } Err(e) => { tracing::error!("按时间清理失败: {}", e); - return Err(format!("按时间清理失败: {}", e)); + return Err(format!("按时间清理失败: {e}")); } } } @@ -576,7 +576,7 @@ pub async fn cleanup_flows( } Err(e) => { tracing::error!("按数量清理失败: {}", e); - return Err(format!("按数量清理失败: {}", e)); + return Err(format!("按数量清理失败: {e}")); } } } @@ -767,14 +767,14 @@ pub async fn create_test_flows( }), messages: vec![Message { role: MessageRole::User, - content: crate::flow_monitor::MessageContent::Text(format!("测试消息 {}", i)), + content: crate::flow_monitor::MessageContent::Text(format!("测试消息 {i}")), tool_calls: None, tool_result: None, name: None, }], system_prompt: None, tools: None, - model: format!("gpt-4-test-{}", i), + model: format!("gpt-4-test-{i}"), original_model: None, parameters: RequestParameters { temperature: Some(0.7), @@ -792,13 +792,13 @@ pub async fn create_test_flows( let metadata = FlowMetadata { provider: ProviderType::OpenAI, provider_id: Some("openai".to_string()), - credential_id: Some(format!("test-cred-{}", i)), - credential_name: Some(format!("测试凭证 {}", i)), + credential_id: Some(format!("test-cred-{i}")), + credential_name: Some(format!("测试凭证 {i}")), retry_count: 0, client_info: ClientInfo { ip: Some("127.0.0.1".to_string()), user_agent: Some("test-agent".to_string()), - request_id: Some(format!("test-req-{}", i)), + request_id: Some(format!("test-req-{i}")), }, routing_info: RoutingInfo { target_url: Some("https://api.openai.com".to_string()), @@ -819,7 +819,7 @@ pub async fn create_test_flows( body: serde_json::json!({ "choices": [{"message": {"role": "assistant", "content": format!("测试响应 {}", i)}}] }), - content: format!("测试响应 {}", i), + content: format!("测试响应 {i}"), thinking: None, tool_calls: Vec::new(), usage: crate::flow_monitor::TokenUsage { @@ -1120,7 +1120,7 @@ pub async fn query_flows_with_expression( request.page_size, ) .await - .map_err(|e| format!("查询 Flow 失败: {}", e)) + .map_err(|e| format!("查询 Flow 失败: {e}")) } // ============================================================================ @@ -1174,7 +1174,7 @@ pub async fn intercept_config_set( .0 .update_config(config) .await - .map_err(|e| format!("设置拦截器配置失败: {}", e)) + .map_err(|e| format!("设置拦截器配置失败: {e}")) } /// 继续处理被拦截的 Flow @@ -1200,17 +1200,15 @@ pub async fn intercept_continue( // 确定修改数据 let modified = if let Some(req) = modified_request { Some(ModifiedData::Request(req)) - } else if let Some(resp) = modified_response { - Some(ModifiedData::Response(resp)) } else { - None + modified_response.map(ModifiedData::Response) }; interceptor .0 .continue_flow(&flow_id, modified) .await - .map_err(|e| format!("继续处理 Flow 失败: {}", e)) + .map_err(|e| format!("继续处理 Flow 失败: {e}")) } /// 取消被拦截的 Flow @@ -1233,7 +1231,7 @@ pub async fn intercept_cancel( .0 .cancel_flow(&flow_id) .await - .map_err(|e| format!("取消 Flow 失败: {}", e)) + .map_err(|e| format!("取消 Flow 失败: {e}")) } /// 获取被拦截的 Flow 详情 @@ -1354,7 +1352,7 @@ pub async fn intercept_set_editing( .0 .set_editing(&flow_id) .await - .map_err(|e| format!("设置编辑状态失败: {}", e)) + .map_err(|e| format!("设置编辑状态失败: {e}")) } /// 订阅拦截事件 @@ -1446,7 +1444,7 @@ pub async fn replay_flow( .0 .replay(&request.flow_id, request.config) .await - .map_err(|e| format!("重放 Flow 失败: {}", e)) + .map_err(|e| format!("重放 Flow 失败: {e}")) } /// 批量重放多个 Flow @@ -1508,7 +1506,7 @@ pub async fn diff_flows( .0 .get_flow(&request.left_flow_id) .await - .map_err(|e| format!("获取左侧 Flow 失败: {}", e))? + .map_err(|e| format!("获取左侧 Flow 失败: {e}"))? .ok_or_else(|| format!("左侧 Flow 不存在: {}", request.left_flow_id))?; // 获取右侧 Flow @@ -1516,7 +1514,7 @@ pub async fn diff_flows( .0 .get_flow(&request.right_flow_id) .await - .map_err(|e| format!("获取右侧 Flow 失败: {}", e))? + .map_err(|e| format!("获取右侧 Flow 失败: {e}"))? .ok_or_else(|| format!("右侧 Flow 不存在: {}", request.right_flow_id))?; // 执行差异对比 @@ -1685,7 +1683,7 @@ pub async fn create_session( session_manager .0 .create_session(&request.name, request.description.as_deref()) - .map_err(|e| format!("创建会话失败: {}", e)) + .map_err(|e| format!("创建会话失败: {e}")) } /// 获取会话详情 @@ -1707,7 +1705,7 @@ pub async fn get_session( session_manager .0 .get_session(&session_id) - .map_err(|e| format!("获取会话失败: {}", e)) + .map_err(|e| format!("获取会话失败: {e}")) } /// 列出所有会话 @@ -1729,7 +1727,7 @@ pub async fn list_sessions( session_manager .0 .list_sessions(include_archived.unwrap_or(false)) - .map_err(|e| format!("列出会话失败: {}", e)) + .map_err(|e| format!("列出会话失败: {e}")) } /// 添加 Flow 到会话 @@ -1753,7 +1751,7 @@ pub async fn add_flow_to_session( session_manager .0 .add_flow(&session_id, &flow_id) - .map_err(|e| format!("添加 Flow 到会话失败: {}", e)) + .map_err(|e| format!("添加 Flow 到会话失败: {e}")) } /// 从会话移除 Flow @@ -1777,7 +1775,7 @@ pub async fn remove_flow_from_session( session_manager .0 .remove_flow(&session_id, &flow_id) - .map_err(|e| format!("从会话移除 Flow 失败: {}", e)) + .map_err(|e| format!("从会话移除 Flow 失败: {e}")) } /// 更新会话信息 @@ -1803,7 +1801,7 @@ pub async fn update_session( request.name.as_deref(), request.description.as_ref().map(|d| d.as_deref()), ) - .map_err(|e| format!("更新会话失败: {}", e)) + .map_err(|e| format!("更新会话失败: {e}")) } /// 归档会话 @@ -1825,7 +1823,7 @@ pub async fn archive_session( session_manager .0 .archive_session(&session_id) - .map_err(|e| format!("归档会话失败: {}", e)) + .map_err(|e| format!("归档会话失败: {e}")) } /// 取消归档会话 @@ -1845,7 +1843,7 @@ pub async fn unarchive_session( session_manager .0 .unarchive_session(&session_id) - .map_err(|e| format!("取消归档会话失败: {}", e)) + .map_err(|e| format!("取消归档会话失败: {e}")) } /// 删除会话 @@ -1867,7 +1865,7 @@ pub async fn delete_session( session_manager .0 .delete_session(&session_id) - .map_err(|e| format!("删除会话失败: {}", e)) + .map_err(|e| format!("删除会话失败: {e}")) } /// 导出会话 @@ -1892,7 +1890,7 @@ pub async fn export_session( let flow_ids = session_manager .0 .get_session_flow_ids(&request.session_id) - .map_err(|e| format!("获取会话 Flow 列表失败: {}", e))?; + .map_err(|e| format!("获取会话 Flow 列表失败: {e}"))?; // 获取所有 Flow let mut flows = Vec::new(); @@ -1906,7 +1904,7 @@ pub async fn export_session( session_manager .0 .export_session(&request.session_id, &flows, request.format) - .map_err(|e| format!("导出会话失败: {}", e)) + .map_err(|e| format!("导出会话失败: {e}")) } /// 获取会话中的 Flow 数量 @@ -1926,7 +1924,7 @@ pub async fn get_session_flow_count( session_manager .0 .get_session_flow_count(&session_id) - .map_err(|e| format!("获取会话 Flow 数量失败: {}", e)) + .map_err(|e| format!("获取会话 Flow 数量失败: {e}")) } /// 检查 Flow 是否在会话中 @@ -1948,7 +1946,7 @@ pub async fn is_flow_in_session( session_manager .0 .is_flow_in_session(&session_id, &flow_id) - .map_err(|e| format!("检查 Flow 是否在会话中失败: {}", e)) + .map_err(|e| format!("检查 Flow 是否在会话中失败: {e}")) } /// 获取 Flow 所属的会话列表 @@ -1968,7 +1966,7 @@ pub async fn get_sessions_for_flow( session_manager .0 .get_sessions_for_flow(&flow_id) - .map_err(|e| format!("获取 Flow 所属会话失败: {}", e)) + .map_err(|e| format!("获取 Flow 所属会话失败: {e}")) } /// 获取自动会话检测配置 @@ -2107,7 +2105,7 @@ pub async fn save_quick_filter( request.description.as_deref(), request.group.as_deref(), ) - .map_err(|e| format!("保存快速过滤器失败: {}", e)) + .map_err(|e| format!("保存快速过滤器失败: {e}")) } /// 获取快速过滤器 @@ -2127,7 +2125,7 @@ pub async fn get_quick_filter( quick_filter_manager .0 .get(&id) - .map_err(|e| format!("获取快速过滤器失败: {}", e)) + .map_err(|e| format!("获取快速过滤器失败: {e}")) } /// 更新快速过滤器 @@ -2157,7 +2155,7 @@ pub async fn update_quick_filter( quick_filter_manager .0 .update(&request.id, updates) - .map_err(|e| format!("更新快速过滤器失败: {}", e)) + .map_err(|e| format!("更新快速过滤器失败: {e}")) } /// 删除快速过滤器 @@ -2179,7 +2177,7 @@ pub async fn delete_quick_filter( quick_filter_manager .0 .delete(&id) - .map_err(|e| format!("删除快速过滤器失败: {}", e)) + .map_err(|e| format!("删除快速过滤器失败: {e}")) } /// 列出所有快速过滤器 @@ -2199,7 +2197,7 @@ pub async fn list_quick_filters( quick_filter_manager .0 .list() - .map_err(|e| format!("列出快速过滤器失败: {}", e)) + .map_err(|e| format!("列出快速过滤器失败: {e}")) } /// 按分组列出快速过滤器 @@ -2221,7 +2219,7 @@ pub async fn list_quick_filters_by_group( quick_filter_manager .0 .list_by_group(group.as_deref()) - .map_err(|e| format!("按分组列出快速过滤器失败: {}", e)) + .map_err(|e| format!("按分组列出快速过滤器失败: {e}")) } /// 列出所有分组 @@ -2241,7 +2239,7 @@ pub async fn list_quick_filter_groups( quick_filter_manager .0 .list_groups() - .map_err(|e| format!("列出快速过滤器分组失败: {}", e)) + .map_err(|e| format!("列出快速过滤器分组失败: {e}")) } /// 导出快速过滤器 @@ -2263,7 +2261,7 @@ pub async fn export_quick_filters( quick_filter_manager .0 .export(include_presets.unwrap_or(false)) - .map_err(|e| format!("导出快速过滤器失败: {}", e)) + .map_err(|e| format!("导出快速过滤器失败: {e}")) } /// 导入快速过滤器 @@ -2285,7 +2283,7 @@ pub async fn import_quick_filters( quick_filter_manager .0 .import(&request.data, request.overwrite) - .map_err(|e| format!("导入快速过滤器失败: {}", e)) + .map_err(|e| format!("导入快速过滤器失败: {e}")) } /// 按名称查找快速过滤器 @@ -2305,7 +2303,7 @@ pub async fn find_quick_filter_by_name( quick_filter_manager .0 .find_by_name(&name) - .map_err(|e| format!("查找快速过滤器失败: {}", e)) + .map_err(|e| format!("查找快速过滤器失败: {e}")) } // ============================================================================ @@ -2355,7 +2353,7 @@ pub async fn export_flow_as_code( .0 .get_flow(&request.flow_id) .await - .map_err(|e| format!("获取 Flow 失败: {}", e))? + .map_err(|e| format!("获取 Flow 失败: {e}"))? .ok_or_else(|| format!("Flow 不存在: {}", request.flow_id))?; // 导出为代码 @@ -2511,7 +2509,7 @@ pub async fn add_bookmark( request.name.as_deref(), request.group.as_deref(), ) - .map_err(|e| format!("添加书签失败: {}", e)) + .map_err(|e| format!("添加书签失败: {e}")) } /// 获取书签 @@ -2531,7 +2529,7 @@ pub async fn get_bookmark( bookmark_manager .0 .get(&bookmark_id) - .map_err(|e| format!("获取书签失败: {}", e)) + .map_err(|e| format!("获取书签失败: {e}")) } /// 根据 Flow ID 获取书签 @@ -2551,7 +2549,7 @@ pub async fn get_bookmark_by_flow_id( bookmark_manager .0 .get_by_flow_id(&flow_id) - .map_err(|e| format!("获取书签失败: {}", e)) + .map_err(|e| format!("获取书签失败: {e}")) } /// 移除书签 @@ -2573,7 +2571,7 @@ pub async fn remove_bookmark( bookmark_manager .0 .remove(&bookmark_id) - .map_err(|e| format!("移除书签失败: {}", e)) + .map_err(|e| format!("移除书签失败: {e}")) } /// 根据 Flow ID 移除书签 @@ -2593,7 +2591,7 @@ pub async fn remove_bookmark_by_flow_id( bookmark_manager .0 .remove_by_flow_id(&flow_id) - .map_err(|e| format!("移除书签失败: {}", e)) + .map_err(|e| format!("移除书签失败: {e}")) } /// 更新书签 @@ -2617,7 +2615,7 @@ pub async fn update_bookmark( request.name.as_ref().map(|n| n.as_deref()), request.group.as_ref().map(|g| g.as_deref()), ) - .map_err(|e| format!("更新书签失败: {}", e)) + .map_err(|e| format!("更新书签失败: {e}")) } /// 列出所有书签 @@ -2639,7 +2637,7 @@ pub async fn list_bookmarks( bookmark_manager .0 .list(group.as_deref()) - .map_err(|e| format!("列出书签失败: {}", e)) + .map_err(|e| format!("列出书签失败: {e}")) } /// 列出所有书签分组 @@ -2659,7 +2657,7 @@ pub async fn list_bookmark_groups( bookmark_manager .0 .list_groups() - .map_err(|e| format!("列出书签分组失败: {}", e)) + .map_err(|e| format!("列出书签分组失败: {e}")) } /// 检查 Flow 是否已添加书签 @@ -2679,7 +2677,7 @@ pub async fn is_flow_bookmarked( bookmark_manager .0 .is_bookmarked(&flow_id) - .map_err(|e| format!("检查书签状态失败: {}", e)) + .map_err(|e| format!("检查书签状态失败: {e}")) } /// 获取书签数量 @@ -2697,7 +2695,7 @@ pub async fn get_bookmark_count( bookmark_manager .0 .count() - .map_err(|e| format!("获取书签数量失败: {}", e)) + .map_err(|e| format!("获取书签数量失败: {e}")) } /// 导出书签 @@ -2717,7 +2715,7 @@ pub async fn export_bookmarks( bookmark_manager .0 .export() - .map_err(|e| format!("导出书签失败: {}", e)) + .map_err(|e| format!("导出书签失败: {e}")) } /// 导入书签 @@ -2739,7 +2737,7 @@ pub async fn import_bookmarks( bookmark_manager .0 .import(&request.data, request.overwrite) - .map_err(|e| format!("导入书签失败: {}", e)) + .map_err(|e| format!("导入书签失败: {e}")) } /// 切换书签状态 @@ -2767,19 +2765,19 @@ pub async fn toggle_bookmark( let is_bookmarked = bookmark_manager .0 .is_bookmarked(&flow_id) - .map_err(|e| format!("检查书签状态失败: {}", e))?; + .map_err(|e| format!("检查书签状态失败: {e}"))?; if is_bookmarked { bookmark_manager .0 .remove_by_flow_id(&flow_id) - .map_err(|e| format!("移除书签失败: {}", e))?; + .map_err(|e| format!("移除书签失败: {e}"))?; Ok(None) } else { let bookmark = bookmark_manager .0 .add(&flow_id, name.as_deref(), group.as_deref()) - .map_err(|e| format!("添加书签失败: {}", e))?; + .map_err(|e| format!("添加书签失败: {e}"))?; Ok(Some(bookmark)) } } diff --git a/src-tauri/src/commands/general_chat_cmd.rs b/src-tauri/src/commands/general_chat_cmd.rs index 657dda4e9..46eca18c9 100644 --- a/src-tauri/src/commands/general_chat_cmd.rs +++ b/src-tauri/src/commands/general_chat_cmd.rs @@ -43,8 +43,8 @@ pub async fn general_chat_create_session( metadata, }; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; - GeneralChatDao::create_session(&conn, &session).map_err(|e| format!("创建会话失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; + GeneralChatDao::create_session(&conn, &session).map_err(|e| format!("创建会话失败: {e}"))?; tracing::info!( "[GeneralChat] 创建会话: id={}, name={}", @@ -59,9 +59,9 @@ pub async fn general_chat_create_session( pub async fn general_chat_list_sessions( db: State<'_, DbConnection>, ) -> Result, String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let sessions = - GeneralChatDao::list_sessions(&conn).map_err(|e| format!("获取会话列表失败: {}", e))?; + GeneralChatDao::list_sessions(&conn).map_err(|e| format!("获取会话列表失败: {e}"))?; Ok(sessions) } @@ -77,17 +77,17 @@ pub async fn general_chat_get_session( session_id: String, message_limit: Option, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let session = GeneralChatDao::get_session(&conn, &session_id) - .map_err(|e| format!("获取会话失败: {}", e))? + .map_err(|e| format!("获取会话失败: {e}"))? .ok_or_else(|| "会话不存在".to_string())?; let messages = GeneralChatDao::get_messages(&conn, &session_id, message_limit, None) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; let message_count = GeneralChatDao::get_message_count(&conn, &session_id) - .map_err(|e| format!("获取消息数量失败: {}", e))?; + .map_err(|e| format!("获取消息数量失败: {e}"))?; Ok(SessionDetail { session, @@ -105,10 +105,10 @@ pub async fn general_chat_delete_session( db: State<'_, DbConnection>, session_id: String, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let deleted = GeneralChatDao::delete_session(&conn, &session_id) - .map_err(|e| format!("删除会话失败: {}", e))?; + .map_err(|e| format!("删除会话失败: {e}"))?; if deleted { tracing::info!("[GeneralChat] 删除会话: id={}", session_id); @@ -128,10 +128,10 @@ pub async fn general_chat_rename_session( session_id: String, name: String, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let renamed = GeneralChatDao::rename_session(&conn, &session_id, &name) - .map_err(|e| format!("重命名会话失败: {}", e))?; + .map_err(|e| format!("重命名会话失败: {e}"))?; if renamed { tracing::info!("[GeneralChat] 重命名会话: id={}, name={}", session_id, name); @@ -155,10 +155,10 @@ pub async fn general_chat_get_messages( limit: Option, before_id: Option, ) -> Result, String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let messages = GeneralChatDao::get_messages(&conn, &session_id, limit, before_id.as_deref()) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; Ok(messages) } @@ -186,7 +186,7 @@ pub async fn general_chat_add_message( "user" => MessageRole::User, "assistant" => MessageRole::Assistant, "system" => MessageRole::System, - _ => return Err(format!("无效的消息角色: {}", role)), + _ => return Err(format!("无效的消息角色: {role}")), }; let message = ChatMessage { @@ -200,16 +200,16 @@ pub async fn general_chat_add_message( metadata, }; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; // 检查会话是否存在 if !GeneralChatDao::session_exists(&conn, &session_id) - .map_err(|e| format!("检查会话失败: {}", e))? + .map_err(|e| format!("检查会话失败: {e}"))? { return Err("会话不存在".to_string()); } - GeneralChatDao::add_message(&conn, &message).map_err(|e| format!("添加消息失败: {}", e))?; + GeneralChatDao::add_message(&conn, &message).map_err(|e| format!("添加消息失败: {e}"))?; tracing::debug!( "[GeneralChat] 添加消息: session={}, role={:?}, len={}", @@ -295,17 +295,17 @@ pub async fn general_chat_send_message( }; { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; // 检查会话是否存在 if !GeneralChatDao::session_exists(&conn, &request.session_id) - .map_err(|e| format!("检查会话失败: {}", e))? + .map_err(|e| format!("检查会话失败: {e}"))? { return Err("会话不存在".to_string()); } GeneralChatDao::add_message(&conn, &user_message) - .map_err(|e| format!("保存用户消息失败: {}", e))?; + .map_err(|e| format!("保存用户消息失败: {e}"))?; } // 设置停止标志 @@ -363,9 +363,9 @@ pub async fn general_chat_send_message( }; { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; GeneralChatDao::add_message(&conn, &assistant_message) - .map_err(|e| format!("保存 AI 响应失败: {}", e))?; + .map_err(|e| format!("保存 AI 响应失败: {e}"))?; } // 发送完成事件 @@ -395,8 +395,8 @@ pub async fn general_chat_stop_generation(session_id: String) -> Result, - /// 模型名称(可选) + /// 模型名称(可选,用于指定生成标题的模型) #[serde(default)] pub model: Option, } @@ -430,9 +430,11 @@ pub async fn general_chat_generate_title( request: GenerateTitleRequest, ) -> Result { tracing::info!( - "[GeneralChat] 生成标题: session={}, message_len={}", + "[GeneralChat] 生成标题: session={}, message_len={}, provider={:?}, model={:?}", request.session_id, - request.first_message.len() + request.first_message.len(), + request.provider, + request.model ); // 生成标题的 prompt @@ -441,8 +443,9 @@ pub async fn general_chat_generate_title( request.first_message.chars().take(500).collect::() ); - // 尝试调用 AI 生成标题 - let title = match generate_title_with_ai(&prompt).await { + // 尝试调用 AI 生成标题,使用指定的模型或默认模型 + let model = request.model.as_deref(); + let title = match generate_title_with_ai(&prompt, model).await { Ok(ai_title) => { tracing::info!("[GeneralChat] AI 生成标题成功: {}", ai_title); // 清理 AI 返回的标题(去除引号、换行等) @@ -457,9 +460,9 @@ pub async fn general_chat_generate_title( // 更新数据库中的会话标题 { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; GeneralChatDao::rename_session(&conn, &request.session_id, &title) - .map_err(|e| format!("更新标题失败: {}", e))?; + .map_err(|e| format!("更新标题失败: {e}"))?; } tracing::info!( @@ -472,7 +475,7 @@ pub async fn general_chat_generate_title( } /// 使用 AI 生成标题 -async fn generate_title_with_ai(prompt: &str) -> Result { +async fn generate_title_with_ai(prompt: &str, model: Option<&str>) -> Result { use crate::models::openai::{ChatCompletionRequest, ChatMessage, MessageContent}; use crate::providers::openai_custom::OpenAICustomProvider; @@ -484,7 +487,7 @@ async fn generate_title_with_ai(prompt: &str) -> Result { ); let request = ChatCompletionRequest { - model: "default".to_string(), // 使用默认模型 + model: model.unwrap_or("default").to_string(), messages: vec![ChatMessage { role: "user".to_string(), content: Some(MessageContent::Text(prompt.to_string())), @@ -504,17 +507,17 @@ async fn generate_title_with_ai(prompt: &str) -> Result { let resp = provider .call_api(&request) .await - .map_err(|e| format!("API 调用失败: {}", e))?; + .map_err(|e| format!("API 调用失败: {e}"))?; let status = resp.status(); let body = resp.text().await.unwrap_or_default(); if !status.is_success() { - return Err(format!("API 返回错误: {} - {}", status, body)); + return Err(format!("API 返回错误: {status} - {body}")); } let parsed: serde_json::Value = - serde_json::from_str(&body).map_err(|e| format!("解析响应失败: {}", e))?; + serde_json::from_str(&body).map_err(|e| format!("解析响应失败: {e}"))?; let content = parsed["choices"] .as_array() diff --git a/src-tauri/src/commands/injection_cmd.rs b/src-tauri/src/commands/injection_cmd.rs index 26243783f..e843ebc23 100644 --- a/src-tauri/src/commands/injection_cmd.rs +++ b/src-tauri/src/commands/injection_cmd.rs @@ -140,7 +140,7 @@ pub async fn remove_injection_rule( .rules .iter() .position(|r| r.id == id) - .ok_or_else(|| format!("规则 ID '{}' 不存在", id))?; + .ok_or_else(|| format!("规则 ID '{id}' 不存在"))?; s.config.injection.rules.remove(pos); save_config(&s.config).map_err(|e| e.to_string())?; @@ -162,7 +162,7 @@ pub async fn update_injection_rule( .rules .iter() .position(|r| r.id == id) - .ok_or_else(|| format!("规则 ID '{}' 不存在", id))?; + .ok_or_else(|| format!("规则 ID '{id}' 不存在"))?; s.config.injection.rules[pos] = InjectionRuleConfig { id: rule.id, diff --git a/src-tauri/src/commands/kiro_local.rs b/src-tauri/src/commands/kiro_local.rs index 85fb84189..57a939b23 100644 --- a/src-tauri/src/commands/kiro_local.rs +++ b/src-tauri/src/commands/kiro_local.rs @@ -47,8 +47,7 @@ fn get_aws_sso_cache_dir() -> Result { // 确保目录存在 if !cache_dir.exists() { - fs::create_dir_all(&cache_dir) - .map_err(|e| format!("创建 AWS SSO cache 目录失败: {}", e))?; + fs::create_dir_all(&cache_dir).map_err(|e| format!("创建 AWS SSO cache 目录失败: {e}"))?; } Ok(cache_dir) @@ -57,14 +56,14 @@ fn get_aws_sso_cache_dir() -> Result { /// 计算 clientIdHash(备用方案,使用 SHA256 的前 40 位模拟 SHA1 格式) fn calculate_client_id_hash() -> String { let start_url = "https://view.awsapps.com/start"; - let json_str = format!("{{\"startUrl\":\"{}\"}}", start_url); + let json_str = format!("{{\"startUrl\":\"{start_url}\"}}"); let mut hasher = Sha256::new(); hasher.update(json_str.as_bytes()); let result = hasher.finalize(); // SHA1 是 40 位十六进制,取 SHA256 的前 20 字节(40 位十六进制) - format!("{:x}", result)[..40].to_string() + format!("{result:x}")[..40].to_string() } /// 切换 Kiro 凭证到本地 @@ -85,8 +84,8 @@ pub async fn switch_kiro_to_local( let credential = pool_service .0 .get_by_uuid(&db, &uuid) - .map_err(|e| format!("获取凭证失败: {}", e))? - .ok_or_else(|| format!("找不到凭证: {}", uuid))?; + .map_err(|e| format!("获取凭证失败: {e}"))? + .ok_or_else(|| format!("找不到凭证: {uuid}"))?; // 检查是否为 Kiro 凭证 let creds_file_path = match &credential.credential { @@ -96,32 +95,32 @@ pub async fn switch_kiro_to_local( // 2. 读取凭证文件 let creds_content = - fs::read_to_string(&creds_file_path).map_err(|e| format!("读取凭证文件失败: {}", e))?; + fs::read_to_string(&creds_file_path).map_err(|e| format!("读取凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证文件失败: {}", e))?; + serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证文件失败: {e}"))?; // 3. 获取/生成绑定的 Machine ID let mut fingerprint_store = - KiroFingerprintStore::load().map_err(|e| format!("加载指纹存储失败: {}", e))?; + KiroFingerprintStore::load().map_err(|e| format!("加载指纹存储失败: {e}"))?; let profile_arn = creds.get("profileArn").and_then(|v| v.as_str()); let client_id = creds.get("clientId").and_then(|v| v.as_str()); let binding = fingerprint_store .get_or_create_binding(&uuid, profile_arn, client_id) - .map_err(|e| format!("获取指纹绑定失败: {}", e))?; + .map_err(|e| format!("获取指纹绑定失败: {e}"))?; let machine_id = binding.machine_id.clone(); tracing::info!("[KIRO_LOCAL] 使用 Machine ID: {}", &machine_id[..8]); // 4. 切换系统机器码 let machine_service = - MachineIdService::new().map_err(|e| format!("初始化机器码服务失败: {}", e))?; + MachineIdService::new().map_err(|e| format!("初始化机器码服务失败: {e}"))?; let machine_result = machine_service .set_machine_id(&machine_id) .await - .map_err(|e| format!("切换机器码失败: {}", e))?; + .map_err(|e| format!("切换机器码失败: {e}"))?; if !machine_result.success { if machine_result.requires_admin { @@ -196,10 +195,10 @@ pub async fn switch_kiro_to_local( }; let auth_token_json = serde_json::to_string_pretty(&auth_token) - .map_err(|e| format!("序列化 auth token 失败: {}", e))?; + .map_err(|e| format!("序列化 auth token 失败: {e}"))?; fs::write(&auth_token_path, &auth_token_json) - .map_err(|e| format!("写入 kiro-auth-token.json 失败: {}", e))?; + .map_err(|e| format!("写入 kiro-auth-token.json 失败: {e}"))?; tracing::info!("[KIRO_LOCAL] 已写入 kiro-auth-token.json"); @@ -220,12 +219,12 @@ pub async fn switch_kiro_to_local( ], }; - let registration_path = cache_dir.join(format!("{}.json", client_id_hash)); + let registration_path = cache_dir.join(format!("{client_id_hash}.json")); let registration_json = serde_json::to_string_pretty(®istration) - .map_err(|e| format!("序列化客户端注册信息失败: {}", e))?; + .map_err(|e| format!("序列化客户端注册信息失败: {e}"))?; fs::write(®istration_path, ®istration_json) - .map_err(|e| format!("写入客户端注册文件失败: {}", e))?; + .map_err(|e| format!("写入客户端注册文件失败: {e}"))?; tracing::info!( "[KIRO_LOCAL] 已写入客户端注册文件: {}.json", @@ -237,7 +236,7 @@ pub async fn switch_kiro_to_local( // 8. 更新最后切换时间 fingerprint_store .update_last_switched(&uuid) - .map_err(|e| format!("更新切换时间失败: {}", e))?; + .map_err(|e| format!("更新切换时间失败: {e}"))?; let credential_name = credential .name @@ -265,8 +264,8 @@ pub async fn get_kiro_fingerprint_info( let credential = pool_service .0 .get_by_uuid(&db, &uuid) - .map_err(|e| format!("获取凭证失败: {}", e))? - .ok_or_else(|| format!("找不到凭证: {}", uuid))?; + .map_err(|e| format!("获取凭证失败: {e}"))? + .ok_or_else(|| format!("找不到凭证: {uuid}"))?; // 检查是否为 Kiro 凭证 let creds_file_path = match &credential.credential { @@ -276,20 +275,20 @@ pub async fn get_kiro_fingerprint_info( // 读取凭证文件 let creds_content = - fs::read_to_string(&creds_file_path).map_err(|e| format!("读取凭证文件失败: {}", e))?; + fs::read_to_string(&creds_file_path).map_err(|e| format!("读取凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证文件失败: {}", e))?; + serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证文件失败: {e}"))?; // 获取指纹绑定 let mut fingerprint_store = - KiroFingerprintStore::load().map_err(|e| format!("加载指纹存储失败: {}", e))?; + KiroFingerprintStore::load().map_err(|e| format!("加载指纹存储失败: {e}"))?; let profile_arn = creds.get("profileArn").and_then(|v| v.as_str()); let client_id = creds.get("clientId").and_then(|v| v.as_str()); let binding = fingerprint_store .get_or_create_binding(&uuid, profile_arn, client_id) - .map_err(|e| format!("获取指纹绑定失败: {}", e))?; + .map_err(|e| format!("获取指纹绑定失败: {e}"))?; let auth_method = creds .get("authMethod") @@ -331,9 +330,9 @@ pub async fn get_local_kiro_credential_uuid( } let local_content = - fs::read_to_string(&auth_token_path).map_err(|e| format!("读取本地凭证文件失败: {}", e))?; + fs::read_to_string(&auth_token_path).map_err(|e| format!("读取本地凭证文件失败: {e}"))?; let local_creds: serde_json::Value = - serde_json::from_str(&local_content).map_err(|e| format!("解析本地凭证文件失败: {}", e))?; + serde_json::from_str(&local_content).map_err(|e| format!("解析本地凭证文件失败: {e}"))?; let local_access_token = local_creds.get("accessToken").and_then(|v| v.as_str()); let local_refresh_token = local_creds.get("refreshToken").and_then(|v| v.as_str()); @@ -344,9 +343,7 @@ pub async fn get_local_kiro_credential_uuid( // 获取所有 Kiro 凭证 let overview = pool_service.0.get_overview(&db)?; - let kiro_pool = overview - .iter() - .find(|p| p.provider_type.to_string() == "kiro"); + let kiro_pool = overview.iter().find(|p| p.provider_type == "kiro"); if let Some(pool) = kiro_pool { for cred_display in &pool.credentials { diff --git a/src-tauri/src/commands/machine_id_cmd.rs b/src-tauri/src/commands/machine_id_cmd.rs index ac3ce8eeb..5c2c38210 100644 --- a/src-tauri/src/commands/machine_id_cmd.rs +++ b/src-tauri/src/commands/machine_id_cmd.rs @@ -161,7 +161,7 @@ pub async fn clear_machine_id_override() -> Result { }), Err(e) => Ok(MachineIdResult { success: false, - message: format!("Failed to remove override: {}", e), + message: format!("Failed to remove override: {e}"), requires_restart: false, requires_admin: false, new_machine_id: None, @@ -185,11 +185,10 @@ pub async fn clear_machine_id_override() -> Result { #[tauri::command] pub async fn copy_machine_id_to_clipboard(machine_id: String) -> Result { use arboard::Clipboard; - let mut clipboard = - Clipboard::new().map_err(|e| format!("Failed to access clipboard: {}", e))?; + let mut clipboard = Clipboard::new().map_err(|e| format!("Failed to access clipboard: {e}"))?; clipboard .set_text(machine_id) - .map_err(|e| format!("Failed to copy to clipboard: {}", e))?; + .map_err(|e| format!("Failed to copy to clipboard: {e}"))?; Ok(true) } @@ -198,11 +197,10 @@ pub async fn copy_machine_id_to_clipboard(machine_id: String) -> Result Result { use arboard::Clipboard; - let mut clipboard = - Clipboard::new().map_err(|e| format!("Failed to access clipboard: {}", e))?; + let mut clipboard = Clipboard::new().map_err(|e| format!("Failed to access clipboard: {e}"))?; let text = clipboard .get_text() - .map_err(|e| format!("Failed to read from clipboard: {}", e))?; + .map_err(|e| format!("Failed to read from clipboard: {e}"))?; // 基本验证 let cleaned = text.replace("-", "").replace(" ", "").trim().to_lowercase(); diff --git a/src-tauri/src/commands/model_cmd.rs b/src-tauri/src/commands/model_cmd.rs index 541b600dc..031ef54c2 100644 --- a/src-tauri/src/commands/model_cmd.rs +++ b/src-tauri/src/commands/model_cmd.rs @@ -40,7 +40,7 @@ pub async fn refresh_credential_models( let conn = db.lock().map_err(|e| e.to_string())?; ProviderPoolDao::get_by_uuid(&conn, &credential_uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("凭证不存在: {}", credential_uuid))? + .ok_or_else(|| format!("凭证不存在: {credential_uuid}"))? }; tracing::info!( @@ -127,7 +127,7 @@ pub async fn refresh_all_credential_models( model_service.update_credential_models(&db, &credential.uuid, models.clone()) { tracing::error!("[REFRESH_ALL] 更新数据库失败: {}", e); - Err(format!("更新数据库失败: {}", e)) + Err(format!("更新数据库失败: {e}")) } else { tracing::info!("[REFRESH_ALL] 成功刷新 {} 个模型", models.len()); Ok(models) diff --git a/src-tauri/src/commands/model_registry_cmd.rs b/src-tauri/src/commands/model_registry_cmd.rs index 7edaccd80..8ca93925a 100644 --- a/src-tauri/src/commands/model_registry_cmd.rs +++ b/src-tauri/src/commands/model_registry_cmd.rs @@ -136,7 +136,7 @@ pub async fn get_models_by_tier( let tier: ModelTier = tier .parse() - .map_err(|_| format!("无效的服务等级: {}", tier))?; + .map_err(|_| format!("无效的服务等级: {tier}"))?; Ok(service.get_models_by_tier(tier).await) } @@ -227,13 +227,13 @@ pub async fn fetch_provider_models_auto( let provider = api_key_service .0 .get_provider(&db, &provider_id)? - .ok_or_else(|| format!("Provider 不存在: {}", provider_id))?; + .ok_or_else(|| format!("Provider 不存在: {provider_id}"))?; // 获取 API Key let api_key = api_key_service .0 .get_next_api_key(&db, &provider_id)? - .ok_or_else(|| format!("Provider {} 没有可用的 API Key", provider_id))?; + .ok_or_else(|| format!("Provider {provider_id} 没有可用的 API Key"))?; // 获取 API Host let api_host = provider.provider.api_host.clone(); diff --git a/src-tauri/src/commands/models_cmd.rs b/src-tauri/src/commands/models_cmd.rs index c3a19f646..802b34773 100644 --- a/src-tauri/src/commands/models_cmd.rs +++ b/src-tauri/src/commands/models_cmd.rs @@ -162,7 +162,7 @@ pub async fn add_model_to_provider( if let Some(provider_config) = state.config.models.providers.get_mut(&provider) { // 检查是否已存在 if provider_config.models.iter().any(|m| m.id == model_id) { - return Err(format!("模型 {} 已存在于 {} 中", model_id, provider)); + return Err(format!("模型 {model_id} 已存在于 {provider} 中")); } provider_config.models.push(ModelInfo { id: model_id, @@ -170,7 +170,7 @@ pub async fn add_model_to_provider( enabled: true, }); } else { - return Err(format!("Provider {} 不存在", provider)); + return Err(format!("Provider {provider} 不存在")); } save_config(&state.config).map_err(|e| e.to_string())?; @@ -189,7 +189,7 @@ pub async fn remove_model_from_provider( if let Some(provider_config) = state.config.models.providers.get_mut(&provider) { provider_config.models.retain(|m| m.id != model_id); } else { - return Err(format!("Provider {} 不存在", provider)); + return Err(format!("Provider {provider} 不存在")); } save_config(&state.config).map_err(|e| e.to_string())?; @@ -210,10 +210,10 @@ pub async fn toggle_model_enabled( if let Some(model) = provider_config.models.iter_mut().find(|m| m.id == model_id) { model.enabled = enabled; } else { - return Err(format!("模型 {} 不存在于 {} 中", model_id, provider)); + return Err(format!("模型 {model_id} 不存在于 {provider} 中")); } } else { - return Err(format!("Provider {} 不存在", provider)); + return Err(format!("Provider {provider} 不存在")); } save_config(&state.config).map_err(|e| e.to_string())?; @@ -230,7 +230,7 @@ pub async fn add_provider( let mut state = app_state.write().await; if state.config.models.providers.contains_key(&provider_id) { - return Err(format!("Provider {} 已存在", provider_id)); + return Err(format!("Provider {provider_id} 已存在")); } state.config.models.providers.insert( @@ -254,7 +254,7 @@ pub async fn remove_provider( let mut state = app_state.write().await; if state.config.models.providers.remove(&provider_id).is_none() { - return Err(format!("Provider {} 不存在", provider_id)); + return Err(format!("Provider {provider_id} 不存在")); } save_config(&state.config).map_err(|e| e.to_string())?; diff --git a/src-tauri/src/commands/music_cmd.rs b/src-tauri/src/commands/music_cmd.rs index 6e8adc860..bb1941e2d 100644 --- a/src-tauri/src/commands/music_cmd.rs +++ b/src-tauri/src/commands/music_cmd.rs @@ -111,17 +111,16 @@ pub async fn analyze_midi(midi_path: String) -> Result Resul .arg(&mp3_path) .arg(&output_path) .output() - .map_err(|e| format!("Failed to execute Python script: {}", e))?; + .map_err(|e| format!("Failed to execute Python script: {e}"))?; if !output.status.success() { let error = String::from_utf8_lossy(&output.stderr); - return Err(format!("MP3 to MIDI conversion failed: {}", error)); + return Err(format!("MP3 to MIDI conversion failed: {error}")); } Ok(output_path) @@ -149,10 +148,10 @@ pub async fn convert_mp3_to_midi(mp3_path: String, output_path: String) -> Resul /// 加载资源文件 #[tauri::command] pub async fn load_music_resource(resource_name: String) -> Result { - let resource_path = get_resource_path(&format!("music/{}", resource_name))?; + let resource_path = get_resource_path(&format!("music/{resource_name}"))?; std::fs::read_to_string(&resource_path) - .map_err(|e| format!("Failed to read resource file: {}", e)) + .map_err(|e| format!("Failed to read resource file: {e}")) } /// 获取资源文件路径 @@ -160,7 +159,7 @@ fn get_resource_path(relative_path: &str) -> Result { // 在开发环境中,资源文件在 src-tauri/resources/ // 在生产环境中,资源文件会被打包到应用程序包中 let mut path = - std::env::current_exe().map_err(|e| format!("Failed to get executable path: {}", e))?; + std::env::current_exe().map_err(|e| format!("Failed to get executable path: {e}"))?; path.pop(); // 移除可执行文件名 @@ -185,7 +184,7 @@ fn get_resource_path(relative_path: &str) -> Result { if dev_path.exists() { return Ok(dev_path); } - return Err(format!("Resource not found: {}", relative_path)); + return Err(format!("Resource not found: {relative_path}")); } Ok(path) @@ -200,11 +199,11 @@ pub async fn install_python_dependencies() -> Result { .arg("install") .args(&packages) .output() - .map_err(|e| format!("Failed to install packages: {}", e))?; + .map_err(|e| format!("Failed to install packages: {e}"))?; if !output.status.success() { let error = String::from_utf8_lossy(&output.stderr); - return Err(format!("Installation failed: {}", error)); + return Err(format!("Installation failed: {error}")); } Ok("Dependencies installed successfully".to_string()) diff --git a/src-tauri/src/commands/network_cmd.rs b/src-tauri/src/commands/network_cmd.rs index 671e6608a..f0a5a0d3c 100644 --- a/src-tauri/src/commands/network_cmd.rs +++ b/src-tauri/src/commands/network_cmd.rs @@ -144,7 +144,7 @@ pub fn get_accessible_host(listen_host: &str) -> String { /// 格式为 `http://{host}:{port}` 的 URL pub fn get_accessible_url(listen_host: &str, port: u16) -> String { let host = get_accessible_host(listen_host); - format!("http://{}:{}", host, port) + format!("http://{host}:{port}") } /// 根据监听地址生成本地访问的 URL @@ -164,7 +164,7 @@ pub fn get_local_url(listen_host: &str, port: u16) -> String { "0.0.0.0" | "localhost" => "127.0.0.1".to_string(), _ => listen_host.to_string(), }; - format!("http://{}:{}", host, port) + format!("http://{host}:{port}") } #[cfg(test)] diff --git a/src-tauri/src/commands/orchestrator_cmd.rs b/src-tauri/src/commands/orchestrator_cmd.rs index a68d5774c..47bc12985 100644 --- a/src-tauri/src/commands/orchestrator_cmd.rs +++ b/src-tauri/src/commands/orchestrator_cmd.rs @@ -52,10 +52,8 @@ pub async fn init_orchestrator( // 从数据库加载凭证并同步到 orchestrator let credentials = { - let conn = db - .lock() - .map_err(|e| format!("获取数据库连接失败: {}", e))?; - ProviderPoolDao::get_all(&conn).map_err(|e| format!("获取凭证列表失败: {}", e))? + let conn = db.lock().map_err(|e| format!("获取数据库连接失败: {e}"))?; + ProviderPoolDao::get_all(&conn).map_err(|e| format!("获取凭证列表失败: {e}"))? }; // 转换凭证格式 @@ -226,7 +224,7 @@ pub async fn get_tier_models(tier: String) -> Result, String let orchestrator = get_global_orchestrator().ok_or("编排器未初始化")?; let service_tier = - ServiceTier::from_str(&tier).ok_or_else(|| format!("无效的服务等级: {}", tier))?; + ServiceTier::from_str(&tier).ok_or_else(|| format!("无效的服务等级: {tier}"))?; Ok(orchestrator.get_models(service_tier).await) } @@ -408,7 +406,7 @@ pub async fn select_model_for_task(tier: String, task: String) -> Result TaskHint::Coding, @@ -445,7 +443,7 @@ pub fn list_service_tiers() -> Vec { ServiceTier::all() .iter() .map(|t| ServiceTierInfo { - id: format!("{:?}", t).to_lowercase(), + id: format!("{t:?}").to_lowercase(), display_name: t.display_name().to_string(), description: t.description().to_string(), level: t.level(), diff --git a/src-tauri/src/commands/plugin_cmd.rs b/src-tauri/src/commands/plugin_cmd.rs index 2211be81c..a10f59945 100644 --- a/src-tauri/src/commands/plugin_cmd.rs +++ b/src-tauri/src/commands/plugin_cmd.rs @@ -22,7 +22,7 @@ use super::plugin_install_cmd::PluginInstallerState; /// 前端调试日志命令 #[tauri::command] pub fn frontend_debug_log(message: String) { - println!("[Frontend] {}", message); + println!("[Frontend] {message}"); } /// 插件管理器状态 @@ -151,7 +151,7 @@ pub async fn get_plugins_dir( ) -> Result { let manager = state.0.read().await; let dir = manager.plugins_dir().to_string_lossy().to_string(); - println!("[get_plugins_dir] 返回: {}", dir); + println!("[get_plugins_dir] 返回: {dir}"); Ok(dir) } @@ -435,7 +435,7 @@ pub async fn read_plugin_manifest_cmd( ); // 输出序列化后的 JSON if let Ok(json) = serde_json::to_string(&manifest) { - println!("[read_plugin_manifest_cmd] JSON: {}", json); + println!("[read_plugin_manifest_cmd] JSON: {json}"); } return Ok(Some(manifest)); } @@ -480,10 +480,10 @@ pub async fn launch_plugin_ui( if let Some(m) = read_plugin_manifest(&installed.install_path) { (m, installed.install_path.clone()) } else { - return Err(format!("插件 {} 不存在", plugin_id)); + return Err(format!("插件 {plugin_id} 不存在")); } } else { - return Err(format!("插件 {} 不存在", plugin_id)); + return Err(format!("插件 {plugin_id} 不存在")); } }; @@ -507,7 +507,7 @@ pub async fn launch_plugin_ui( // 启动二进制文件 std::process::Command::new(&binary_path) .spawn() - .map_err(|e| format!("启动插件失败: {}", e))?; + .map_err(|e| format!("启动插件失败: {e}"))?; Ok(()) } diff --git a/src-tauri/src/commands/plugin_install_cmd.rs b/src-tauri/src/commands/plugin_install_cmd.rs index 07d8512ff..c99ad4097 100644 --- a/src-tauri/src/commands/plugin_install_cmd.rs +++ b/src-tauri/src/commands/plugin_install_cmd.rs @@ -69,7 +69,7 @@ pub async fn install_plugin_from_file( return Ok(InstallResult { success: false, plugin: None, - error: Some(format!("文件不存在: {}", file_path)), + error: Some(format!("文件不存在: {file_path}")), }); } diff --git a/src-tauri/src/commands/plugin_rpc_cmd.rs b/src-tauri/src/commands/plugin_rpc_cmd.rs index 329301140..74b2edeee 100644 --- a/src-tauri/src/commands/plugin_rpc_cmd.rs +++ b/src-tauri/src/commands/plugin_rpc_cmd.rs @@ -133,14 +133,14 @@ pub async fn plugin_rpc_connect( let plugin = plugins .iter() .find(|p| p.id == plugin_id) - .ok_or_else(|| format!("插件 {} 未安装", plugin_id))?; + .ok_or_else(|| format!("插件 {plugin_id} 未安装"))?; // 读取插件 manifest let manifest_path = plugin.install_path.join("plugin.json"); - let manifest_content = std::fs::read_to_string(&manifest_path) - .map_err(|e| format!("读取 manifest 失败: {}", e))?; - let manifest: Value = serde_json::from_str(&manifest_content) - .map_err(|e| format!("解析 manifest 失败: {}", e))?; + let manifest_content = + std::fs::read_to_string(&manifest_path).map_err(|e| format!("读取 manifest 失败: {e}"))?; + let manifest: Value = + serde_json::from_str(&manifest_content).map_err(|e| format!("解析 manifest 失败: {e}"))?; // 获取二进制文件路径 let _binary_name = manifest["binary"]["binary_name"] @@ -159,11 +159,11 @@ pub async fn plugin_rpc_connect( let binary_filename = manifest["binary"]["platform_binaries"][platform_key] .as_str() - .ok_or_else(|| format!("manifest 中缺少 {} 平台的二进制文件", platform_key))?; + .ok_or_else(|| format!("manifest 中缺少 {platform_key} 平台的二进制文件"))?; let binary_path = plugin.install_path.join(binary_filename); if !binary_path.exists() { - return Err(format!("二进制文件不存在: {:?}", binary_path)); + return Err(format!("二进制文件不存在: {binary_path:?}")); } // 启动进程(使用 tokio::process::Command) @@ -172,7 +172,7 @@ pub async fn plugin_rpc_connect( .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() - .map_err(|e| format!("启动插件进程失败: {}", e))?; + .map_err(|e| format!("启动插件进程失败: {e}"))?; tracing::info!("插件 {} 进程已启动, PID: {:?}", plugin_id, child.id()); @@ -322,7 +322,7 @@ pub async fn plugin_rpc_call( let processes = rpc_state.processes.read().await; let process_arc = processes .get(&plugin_id) - .ok_or_else(|| format!("插件 {} 未连接", plugin_id))? + .ok_or_else(|| format!("插件 {plugin_id} 未连接"))? .clone(); drop(processes); @@ -338,7 +338,7 @@ pub async fn plugin_rpc_call( }; let request_json = - serde_json::to_string(&request).map_err(|e| format!("序列化请求失败: {}", e))?; + serde_json::to_string(&request).map_err(|e| format!("序列化请求失败: {e}"))?; // 创建响应 channel let (response_tx, response_rx) = oneshot::channel(); @@ -355,15 +355,15 @@ pub async fn plugin_rpc_call( stdin .write_all(request_json.as_bytes()) .await - .map_err(|e| format!("发送请求失败: {}", e))?; + .map_err(|e| format!("发送请求失败: {e}"))?; stdin .write_all(b"\n") .await - .map_err(|e| format!("发送换行失败: {}", e))?; + .map_err(|e| format!("发送换行失败: {e}"))?; stdin .flush() .await - .map_err(|e| format!("刷新 stdin 失败: {}", e))?; + .map_err(|e| format!("刷新 stdin 失败: {e}"))?; } // 释放 process lock,让 stdout 读取任务可以处理响应 @@ -378,7 +378,7 @@ pub async fn plugin_rpc_call( let process = process_arc.lock().await; let mut pending = process.pending_requests.lock().await; pending.remove(&request_id); - Err(format!("RPC 调用 {} 超时", method)) + Err(format!("RPC 调用 {method} 超时")) } } } diff --git a/src-tauri/src/commands/provider_pool_cmd.rs b/src-tauri/src/commands/provider_pool_cmd.rs index 422c3bd68..9c9e8eebe 100644 --- a/src-tauri/src/commands/provider_pool_cmd.rs +++ b/src-tauri/src/commands/provider_pool_cmd.rs @@ -41,7 +41,7 @@ fn get_credentials_dir() -> Result { // 确保目录存在 if !app_data_dir.exists() { - fs::create_dir_all(&app_data_dir).map_err(|e| format!("创建凭证存储目录失败: {}", e))?; + fs::create_dir_all(&app_data_dir).map_err(|e| format!("创建凭证存储目录失败: {e}"))?; } Ok(app_data_dir) @@ -60,7 +60,7 @@ fn copy_and_rename_credential_file( // 验证源文件存在 if !source.exists() { - return Err(format!("凭证文件不存在: {}", expanded_source)); + return Err(format!("凭证文件不存在: {expanded_source}")); } // 生成新的文件名:{provider_type}_{uuid}_{timestamp}.json @@ -84,9 +84,9 @@ fn copy_and_rename_credential_file( // 对于 Kiro 凭证,需要合并 clientIdHash 文件中的 client_id/client_secret if provider_type == "kiro" { - let content = fs::read_to_string(source).map_err(|e| format!("读取凭证文件失败: {}", e))?; + let content = fs::read_to_string(source).map_err(|e| format!("读取凭证文件失败: {e}"))?; let mut creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证文件失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证文件失败: {e}"))?; // 检测 refreshToken 是否被截断(仅记录警告,不阻止添加) // 正常的 refreshToken 长度应该在 500+ 字符,如果小于 100 字符则可能被截断 @@ -126,7 +126,7 @@ fn copy_and_rename_credential_file( // 方式1:如果有 clientIdHash,读取对应文件 if let Some(hash) = creds.get("clientIdHash").and_then(|v| v.as_str()) { - let hash_file_path = aws_sso_cache_dir.join(format!("{}.json", hash)); + let hash_file_path = aws_sso_cache_dir.join(format!("{hash}.json")); if hash_file_path.exists() { if let Ok(hash_content) = fs::read_to_string(&hash_file_path) { @@ -213,11 +213,11 @@ fn copy_and_rename_credential_file( // 写入合并后的凭证到副本文件 let merged_content = - serde_json::to_string_pretty(&creds).map_err(|e| format!("序列化凭证失败: {}", e))?; - fs::write(&target_path, merged_content).map_err(|e| format!("写入凭证文件失败: {}", e))?; + serde_json::to_string_pretty(&creds).map_err(|e| format!("序列化凭证失败: {e}"))?; + fs::write(&target_path, merged_content).map_err(|e| format!("写入凭证文件失败: {e}"))?; } else { // 其他类型直接复制 - fs::copy(source, &target_path).map_err(|e| format!("复制凭证文件失败: {}", e))?; + fs::copy(source, &target_path).map_err(|e| format!("复制凭证文件失败: {e}"))?; } // 返回新的文件路径 @@ -235,7 +235,7 @@ fn cleanup_credential_file(file_path: &str) -> Result<(), String> { if canonical_path.starts_with(canonical_dir) { if let Err(e) = fs::remove_file(&canonical_path) { // 只记录警告,不中断删除过程 - println!("Warning: Failed to delete credential file: {}", e); + println!("Warning: Failed to delete credential file: {e}"); } } } @@ -322,7 +322,7 @@ pub fn update_provider_pool_credential( let conn = db.lock().map_err(|e| e.to_string())?; let current_credential = ProviderPoolDao::get_by_uuid(&conn, &uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("凭证不存在: {}", uuid))?; + .ok_or_else(|| format!("凭证不存在: {uuid}"))?; // 根据凭证类型复制新文件 let new_stored_path = match ¤t_credential.credential { @@ -413,7 +413,7 @@ pub fn update_provider_pool_credential( let conn = db.lock().map_err(|e| e.to_string())?; let mut current_credential = ProviderPoolDao::get_by_uuid(&conn, &uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("凭证不存在: {}", uuid))?; + .ok_or_else(|| format!("凭证不存在: {uuid}"))?; // 更新 api_key 和 base_url match &mut current_credential.credential { @@ -647,7 +647,7 @@ pub fn add_kiro_oauth_credential( fn create_kiro_credential_from_json(json_content: &str) -> Result { // 验证 JSON 格式 let creds: serde_json::Value = - serde_json::from_str(json_content).map_err(|e| format!("JSON 格式无效: {}", e))?; + serde_json::from_str(json_content).map_err(|e| format!("JSON 格式无效: {e}"))?; // 验证必要字段 if creds.get("refreshToken").is_none() { @@ -699,7 +699,7 @@ fn create_kiro_credential_from_json(json_content: &str) -> Result Result Result { // P0 安全修复:不再输出敏感信息(clientIdHash、token 前缀等) let detected_method = provider.detect_auth_method(); - result.push_str(&format!("🎯 检测到的认证方式: {}\n", detected_method)); + result.push_str(&format!("🎯 检测到的认证方式: {detected_method}\n")); result.push_str("\n🚀 尝试刷新 token...\n"); match provider.refresh_token().await { @@ -1070,12 +1070,12 @@ pub async fn debug_kiro_credentials() -> Result { // 不再输出 token 前缀 } Err(e) => { - result.push_str(&format!("❌ Token 刷新失败: {}\n", e)); + result.push_str(&format!("❌ Token 刷新失败: {e}\n")); } } } Err(e) => { - result.push_str(&format!("❌ 凭证加载失败: {}\n", e)); + result.push_str(&format!("❌ 凭证加载失败: {e}\n")); } } @@ -1139,12 +1139,12 @@ pub async fn test_user_credentials() -> Result { json.get("clientIdHash").and_then(|v| v.as_str()).is_some(); let region = json.get("region").and_then(|v| v.as_str()); - result.push_str(&format!("🔑 有 accessToken: {}\n", has_access_token)); - result.push_str(&format!("🔄 有 refreshToken: {}\n", has_refresh_token)); - result.push_str(&format!("📄 authMethod: {:?}\n", auth_method)); + result.push_str(&format!("🔑 有 accessToken: {has_access_token}\n")); + result.push_str(&format!("🔄 有 refreshToken: {has_refresh_token}\n")); + result.push_str(&format!("📄 authMethod: {auth_method:?}\n")); // P0 安全修复:不输出 clientIdHash 值 - result.push_str(&format!("🏷️ 有 clientIdHash: {}\n", has_client_id_hash)); - result.push_str(&format!("🌍 region: {:?}\n", region)); + result.push_str(&format!("🏷️ 有 clientIdHash: {has_client_id_hash}\n")); + result.push_str(&format!("🌍 region: {region:?}\n")); // 使用 KiroProvider 测试加载 result.push_str("\n🔧 使用 KiroProvider 测试加载...\n"); @@ -1172,7 +1172,7 @@ pub async fn test_user_credentials() -> Result { )); let detected_method = provider.detect_auth_method(); - result.push_str(&format!("🎯 检测到的认证方式: {}\n", detected_method)); + result.push_str(&format!("🎯 检测到的认证方式: {detected_method}\n")); result.push_str("\n🚀 尝试刷新 token...\n"); match provider.refresh_token().await { @@ -1184,22 +1184,22 @@ pub async fn test_user_credentials() -> Result { // P0 安全修复:不输出 token 前缀 } Err(e) => { - result.push_str(&format!("❌ Token 刷新失败: {}\n", e)); + result.push_str(&format!("❌ Token 刷新失败: {e}\n")); } } } Err(e) => { - result.push_str(&format!("❌ KiroProvider 加载失败: {}\n", e)); + result.push_str(&format!("❌ KiroProvider 加载失败: {e}\n")); } } } Err(e) => { - result.push_str(&format!("❌ JSON 格式无效: {}\n", e)); + result.push_str(&format!("❌ JSON 格式无效: {e}\n")); } } } Err(e) => { - result.push_str(&format!("❌ 无法读取凭证文件: {}\n", e)); + result.push_str(&format!("❌ 无法读取凭证文件: {e}\n")); } } @@ -1267,7 +1267,7 @@ pub async fn get_antigravity_auth_url_and_wait( let (auth_url, wait_future) = antigravity::start_oauth_server_and_get_url(skip_project_id_fetch.unwrap_or(false)) .await - .map_err(|e| format!("启动 OAuth 服务器失败: {}", e))?; + .map_err(|e| format!("启动 OAuth 服务器失败: {e}"))?; tracing::info!("[Antigravity OAuth] 授权 URL: {}", auth_url); @@ -1328,7 +1328,7 @@ pub async fn start_antigravity_oauth_login( // 启动 OAuth 登录 let result = antigravity::start_oauth_login(skip_project_id_fetch.unwrap_or(false)) .await - .map_err(|e| format!("Antigravity OAuth 登录失败: {}", e))?; + .map_err(|e| format!("Antigravity OAuth 登录失败: {e}"))?; tracing::info!( "[Antigravity OAuth] 登录成功,凭证保存到: {}", @@ -1383,7 +1383,7 @@ pub async fn get_codex_auth_url_and_wait( // 启动服务器并获取授权 URL let (auth_url, wait_future) = codex::start_codex_oauth_server_and_get_url() .await - .map_err(|e| format!("启动 OAuth 服务器失败: {}", e))?; + .map_err(|e| format!("启动 OAuth 服务器失败: {e}"))?; tracing::info!("[Codex OAuth] 授权 URL: {}", auth_url); @@ -1437,7 +1437,7 @@ pub async fn start_codex_oauth_login( // 启动 OAuth 登录 let result = codex::start_codex_oauth_login() .await - .map_err(|e| format!("Codex OAuth 登录失败: {}", e))?; + .map_err(|e| format!("Codex OAuth 登录失败: {e}"))?; tracing::info!( "[Codex OAuth] 登录成功,凭证保存到: {}", @@ -1499,7 +1499,7 @@ pub async fn get_claude_oauth_auth_url_and_wait( // 生成授权参数 let params = claude_oauth::generate_claude_oauth_params() - .map_err(|e| format!("生成授权参数失败: {}", e))?; + .map_err(|e| format!("生成授权参数失败: {e}"))?; tracing::info!("[Claude OAuth] 授权 URL: {}", params.auth_url); @@ -1546,7 +1546,7 @@ pub async fn exchange_claude_oauth_code( &state, ) .await - .map_err(|e| format!("Claude OAuth Token 交换失败: {}", e))?; + .map_err(|e| format!("Claude OAuth Token 交换失败: {e}"))?; tracing::info!( "[Claude OAuth] 登录成功,凭证保存到: {}", @@ -1587,7 +1587,7 @@ pub async fn start_claude_oauth_login( // 生成授权参数并打开浏览器 let params = claude_oauth::start_claude_oauth_login() .await - .map_err(|e| format!("Claude OAuth 登录失败: {}", e))?; + .map_err(|e| format!("Claude OAuth 登录失败: {e}"))?; Ok(ClaudeOAuthParamsResponse { auth_url: params.auth_url, @@ -1631,7 +1631,7 @@ pub async fn claude_oauth_with_cookie( // 执行 Cookie 自动授权 let result = claude_oauth::oauth_with_cookie(&session_key, is_setup) .await - .map_err(|e| format!("Claude Cookie 授权失败: {}", e))?; + .map_err(|e| format!("Claude Cookie 授权失败: {e}"))?; tracing::info!( "[Claude OAuth] Cookie 授权成功,凭证保存到: {}", @@ -1688,7 +1688,7 @@ pub async fn get_kiro_credential_fingerprint( let conn = db.lock().map_err(|e| e.to_string())?; let credential = ProviderPoolDao::get_by_uuid(&conn, &uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("凭证不存在: {}", uuid))?; + .ok_or_else(|| format!("凭证不存在: {uuid}"))?; // 检查是否为 Kiro 凭证 match &credential.credential { @@ -1702,7 +1702,7 @@ pub async fn get_kiro_credential_fingerprint( provider .load_credentials_from_path(&creds_file_path) .await - .map_err(|e| format!("加载凭证失败: {}", e))?; + .map_err(|e| format!("加载凭证失败: {e}"))?; // 确定指纹来源 let (source, profile_arn, client_id) = if provider.credentials.profile_arn.is_some() { @@ -1800,7 +1800,7 @@ pub async fn get_gemini_auth_url_and_wait( // 返回错误,让前端知道需要用户手动输入授权码 // 这不是真正的错误,只是流程需要用户交互 - Err(format!("AUTH_URL:{}", auth_url)) + Err(format!("AUTH_URL:{auth_url}")) } /// 用 Gemini 授权码交换 Token 并添加凭证 @@ -1836,7 +1836,7 @@ pub async fn exchange_gemini_code( // 交换 token 并创建凭证 let result = gemini::exchange_gemini_code_and_create_credentials(&code, &code_verifier) .await - .map_err(|e| format!("交换授权码失败: {}", e))?; + .map_err(|e| format!("交换授权码失败: {e}"))?; tracing::info!( "[Gemini OAuth] 登录成功,凭证保存到: {}", @@ -1883,7 +1883,7 @@ pub async fn start_gemini_oauth_login( // 启动 OAuth 登录 let result = gemini::start_gemini_oauth_login() .await - .map_err(|e| format!("Gemini OAuth 登录失败: {}", e))?; + .map_err(|e| format!("Gemini OAuth 登录失败: {e}"))?; tracing::info!( "[Gemini OAuth] 登录成功,凭证保存到: {}", @@ -1976,7 +1976,7 @@ pub async fn start_kiro_builder_id_login( region: Option, ) -> Result { let region = region.unwrap_or_else(|| "us-east-1".to_string()); - let oidc_base = format!("https://oidc.{}.amazonaws.com", region); + let oidc_base = format!("https://oidc.{region}.amazonaws.com"); let start_url = "https://view.awsapps.com/start"; let scopes = vec![ "codewhisperer:completions", @@ -2001,12 +2001,12 @@ pub async fn start_kiro_builder_id_login( }); let reg_res = client - .post(format!("{}/client/register", oidc_base)) + .post(format!("{oidc_base}/client/register")) .header("Content-Type", "application/json") .json(®_body) .send() .await - .map_err(|e| format!("注册客户端请求失败: {}", e))?; + .map_err(|e| format!("注册客户端请求失败: {e}"))?; if !reg_res.status().is_success() { let err_text = reg_res.text().await.unwrap_or_default(); @@ -2016,14 +2016,14 @@ pub async fn start_kiro_builder_id_login( verification_uri: None, expires_in: None, interval: None, - error: Some(format!("注册客户端失败: {}", err_text)), + error: Some(format!("注册客户端失败: {err_text}")), }); } let reg_data: serde_json::Value = reg_res .json() .await - .map_err(|e| format!("解析注册响应失败: {}", e))?; + .map_err(|e| format!("解析注册响应失败: {e}"))?; let client_id = reg_data["clientId"] .as_str() @@ -2048,12 +2048,12 @@ pub async fn start_kiro_builder_id_login( }); let auth_res = client - .post(format!("{}/device_authorization", oidc_base)) + .post(format!("{oidc_base}/device_authorization")) .header("Content-Type", "application/json") .json(&auth_body) .send() .await - .map_err(|e| format!("设备授权请求失败: {}", e))?; + .map_err(|e| format!("设备授权请求失败: {e}"))?; if !auth_res.status().is_success() { let err_text = auth_res.text().await.unwrap_or_default(); @@ -2063,14 +2063,14 @@ pub async fn start_kiro_builder_id_login( verification_uri: None, expires_in: None, interval: None, - error: Some(format!("设备授权失败: {}", err_text)), + error: Some(format!("设备授权失败: {err_text}")), }); } let auth_data: serde_json::Value = auth_res .json() .await - .map_err(|e| format!("解析授权响应失败: {}", e))?; + .map_err(|e| format!("解析授权响应失败: {e}"))?; let device_code = auth_data["deviceCode"] .as_str() @@ -2160,12 +2160,12 @@ pub async fn poll_kiro_builder_id_auth() -> Result Result Result Result Result Result>() .join("\n") ); @@ -2998,7 +2998,7 @@ pub async fn install_playwright(app: tauri::AppHandle) -> Result { - let error = format!("npm install 执行失败: {}", e); + let error = format!("npm install 执行失败: {e}"); tracing::error!("[Playwright] {}", error); let _ = app.emit( "playwright-install-progress", @@ -3013,7 +3013,7 @@ pub async fn install_playwright(app: tauri::AppHandle) -> Result { - let error = format!("无法启动 npm: {}。请确保已安装 Node.js", e); + let error = format!("无法启动 npm: {e}。请确保已安装 Node.js"); tracing::error!("[Playwright] {}", error); let _ = app.emit( "playwright-install-progress", @@ -3099,7 +3099,7 @@ pub async fn install_playwright(app: tauri::AppHandle) -> Result Result { - let error = format!("Chromium 安装执行失败: {}", e); + let error = format!("Chromium 安装执行失败: {e}"); tracing::error!("[Playwright] {}", error); let _ = app.emit( "playwright-install-progress", @@ -3127,7 +3127,7 @@ pub async fn install_playwright(app: tauri::AppHandle) -> Result { - let error = format!("无法启动 npx: {}", e); + let error = format!("无法启动 npx: {e}"); tracing::error!("[Playwright] {}", error); let _ = app.emit( "playwright-install-progress", @@ -3244,7 +3244,7 @@ pub async fn start_kiro_playwright_login( "github" => "Github", "builderid" => "BuilderId", _ => { - return Err(format!("不支持的登录提供商: {}", provider)); + return Err(format!("不支持的登录提供商: {provider}")); } }; @@ -3279,7 +3279,7 @@ pub async fn start_kiro_playwright_login( // 获取脚本路径 let script_path = get_playwright_script_path(); if !script_path.exists() { - return Err(format!("Playwright 登录脚本不存在: {:?}", script_path)); + return Err(format!("Playwright 登录脚本不存在: {script_path:?}")); } tracing::info!("[Playwright Login] 脚本路径: {:?}", script_path); @@ -3292,7 +3292,7 @@ pub async fn start_kiro_playwright_login( .stderr(std::process::Stdio::piped()) .kill_on_drop(true) .spawn() - .map_err(|e| format!("启动 Playwright 进程失败: {}", e))?; + .map_err(|e| format!("启动 Playwright 进程失败: {e}"))?; let stdin = child.stdin.take().ok_or("无法获取 stdin")?; let stdout = child.stdout.take().ok_or("无法获取 stdout")?; @@ -3311,10 +3311,10 @@ pub async fn start_kiro_playwright_login( reader .read_line(&mut line) .await - .map_err(|e| format!("读取就绪信号失败: {}", e))?; + .map_err(|e| format!("读取就绪信号失败: {e}"))?; let ready_response: serde_json::Value = - serde_json::from_str(&line.trim()).map_err(|e| format!("解析就绪信号失败: {}", e))?; + serde_json::from_str(line.trim()).map_err(|e| format!("解析就绪信号失败: {e}"))?; if ready_response.get("action").and_then(|v| v.as_str()) != Some("ready") { return Err("Playwright 脚本未就绪".to_string()); @@ -3331,20 +3331,20 @@ pub async fn start_kiro_playwright_login( }); let request_str = - serde_json::to_string(&login_request).map_err(|e| format!("序列化请求失败: {}", e))?; + serde_json::to_string(&login_request).map_err(|e| format!("序列化请求失败: {e}"))?; stdin .write_all(request_str.as_bytes()) .await - .map_err(|e| format!("发送请求失败: {}", e))?; + .map_err(|e| format!("发送请求失败: {e}"))?; stdin .write_all(b"\n") .await - .map_err(|e| format!("发送换行失败: {}", e))?; + .map_err(|e| format!("发送换行失败: {e}"))?; stdin .flush() .await - .map_err(|e| format!("刷新 stdin 失败: {}", e))?; + .map_err(|e| format!("刷新 stdin 失败: {e}"))?; tracing::info!("[Playwright Login] 已发送登录请求"); @@ -3418,7 +3418,7 @@ pub async fn start_kiro_playwright_login( *process_guard = None; } - return Err(format!("Playwright 登录失败: {}", error)); + return Err(format!("Playwright 登录失败: {error}")); } break; } @@ -3435,7 +3435,7 @@ pub async fn start_kiro_playwright_login( *process_guard = None; } - return Err(format!("Playwright 错误: {}", error)); + return Err(format!("Playwright 错误: {error}")); } _ => {} } @@ -3451,7 +3451,7 @@ pub async fn start_kiro_playwright_login( let mut process_guard = PLAYWRIGHT_LOGIN_PROCESS.write().await; *process_guard = None; } - return Err(format!("读取响应失败: {}", e)); + return Err(format!("读取响应失败: {e}")); } } } @@ -3483,22 +3483,22 @@ pub async fn start_kiro_playwright_login( }); let token_res = client - .post(format!("{}/oauth/token", KIRO_AUTH_ENDPOINT)) + .post(format!("{KIRO_AUTH_ENDPOINT}/oauth/token")) .header("Content-Type", "application/json") .json(&token_body) .send() .await - .map_err(|e| format!("Token 交换请求失败: {}", e))?; + .map_err(|e| format!("Token 交换请求失败: {e}"))?; if !token_res.status().is_success() { let err_text = token_res.text().await.unwrap_or_default(); - return Err(format!("Token 交换失败: {}", err_text)); + return Err(format!("Token 交换失败: {err_text}")); } let token_data: serde_json::Value = token_res .json() .await - .map_err(|e| format!("解析 Token 响应失败: {}", e))?; + .map_err(|e| format!("解析 Token 响应失败: {e}"))?; tracing::info!("[Playwright Login] Token 交换成功!"); @@ -3524,7 +3524,7 @@ pub async fn start_kiro_playwright_login( // 将凭证 JSON 转换为字符串并创建凭证文件 let json_content = - serde_json::to_string_pretty(&creds_json).map_err(|e| format!("序列化凭证失败: {}", e))?; + serde_json::to_string_pretty(&creds_json).map_err(|e| format!("序列化凭证失败: {e}"))?; let stored_file_path = create_kiro_credential_from_json(&json_content)?; @@ -3598,7 +3598,7 @@ pub async fn start_kiro_social_auth_callback_server(app: tauri::AppHandle) -> Re // 尝试绑定端口 let listener = TcpListener::bind("127.0.0.1:19823") .await - .map_err(|e| format!("无法启动回调服务器: {}", e))?; + .map_err(|e| format!("无法启动回调服务器: {e}"))?; tracing::info!("[Kiro Social Auth] 回调服务器已启动在 127.0.0.1:19823"); @@ -3717,16 +3717,14 @@ mod playwright_tests { // 路径应该包含 ms-playwright assert!( cache_dir.to_string_lossy().contains("ms-playwright"), - "缓存目录应包含 ms-playwright: {:?}", - cache_dir + "缓存目录应包含 ms-playwright: {cache_dir:?}" ); // 路径应该是绝对路径或相对于 home 目录 #[cfg(target_os = "macos")] assert!( cache_dir.to_string_lossy().contains("Library/Caches"), - "macOS 缓存目录应在 Library/Caches 下: {:?}", - cache_dir + "macOS 缓存目录应在 Library/Caches 下: {cache_dir:?}" ); #[cfg(target_os = "windows")] @@ -3762,8 +3760,7 @@ mod playwright_tests { path.contains("chromium") || path.contains("Chromium") || path.contains("chrome"), - "路径应包含 chromium/chrome: {}", - path + "路径应包含 chromium/chrome: {path}" ); } None => { diff --git a/src-tauri/src/commands/route_cmd.rs b/src-tauri/src/commands/route_cmd.rs index 30a51a2b8..5ce88929c 100644 --- a/src-tauri/src/commands/route_cmd.rs +++ b/src-tauri/src/commands/route_cmd.rs @@ -39,12 +39,12 @@ pub async fn get_available_routes( crate::models::route_model::RouteEndpoint { path: "/v1/messages".to_string(), protocol: "claude".to_string(), - url: format!("{}/v1/messages", base_url), + url: format!("{base_url}/v1/messages"), }, crate::models::route_model::RouteEndpoint { path: "/v1/chat/completions".to_string(), protocol: "openai".to_string(), - url: format!("{}/v1/chat/completions", base_url), + url: format!("{base_url}/v1/chat/completions"), }, ], tags: vec!["默认".to_string()], diff --git a/src-tauri/src/commands/screenshot_cmd.rs b/src-tauri/src/commands/screenshot_cmd.rs index 8885870dc..cf3df58ee 100644 --- a/src-tauri/src/commands/screenshot_cmd.rs +++ b/src-tauri/src/commands/screenshot_cmd.rs @@ -77,7 +77,7 @@ pub async fn save_experimental_config( debug!("开始保存配置到文件..."); if let Err(e) = config_manager.save_config(&new_config).await { error!("保存配置失败: {}", e); - return Err(format!("保存配置失败: {}", e)); + return Err(format!("保存配置失败: {e}")); } info!("配置文件保存成功"); @@ -91,14 +91,14 @@ pub async fn save_experimental_config( if let Err(e) = shortcut::register(&app, &experimental_config.screenshot_chat.shortcut) { error!("注册快捷键失败: {}", e); - return Err(format!("注册快捷键失败: {}", e)); + return Err(format!("注册快捷键失败: {e}")); } info!("快捷键注册成功"); } else { info!("截图对话功能已禁用,注销快捷键"); if let Err(e) = shortcut::unregister(&app) { error!("注销快捷键失败: {}", e); - return Err(format!("注销快捷键失败: {}", e)); + return Err(format!("注销快捷键失败: {e}")); } info!("快捷键注销成功"); } @@ -134,7 +134,7 @@ pub async fn start_screenshot(app: AppHandle) -> Result { } Err(e) => { error!("截图失败: {}", e); - Err(format!("截图失败: {}", e)) + Err(format!("截图失败: {e}")) } } } @@ -156,7 +156,7 @@ pub fn validate_shortcut(shortcut_str: String) -> Result { match shortcut::validate(&shortcut_str) { Ok(()) => Ok(true), - Err(e) => Err(format!("{}", e)), + Err(e) => Err(format!("{e}")), } } @@ -183,7 +183,7 @@ pub async fn update_screenshot_shortcut( info!("更新截图快捷键: {}", new_shortcut); // 验证新快捷键格式 - shortcut::validate(&new_shortcut).map_err(|e| format!("快捷键格式无效: {}", e))?; + shortcut::validate(&new_shortcut).map_err(|e| format!("快捷键格式无效: {e}"))?; // 获取当前配置 let mut config = config_manager.config(); @@ -191,7 +191,7 @@ pub async fn update_screenshot_shortcut( // 检查功能是否启用 if config.experimental.screenshot_chat.enabled { // 更新快捷键(原子操作) - shortcut::update(&app, &new_shortcut).map_err(|e| format!("更新快捷键失败: {}", e))?; + shortcut::update(&app, &new_shortcut).map_err(|e| format!("更新快捷键失败: {e}"))?; } // 更新配置 @@ -201,7 +201,7 @@ pub async fn update_screenshot_shortcut( config_manager .save_config(&config) .await - .map_err(|e| format!("保存配置失败: {}", e))?; + .map_err(|e| format!("保存配置失败: {e}"))?; info!("截图快捷键更新成功"); Ok(()) @@ -220,8 +220,7 @@ pub async fn update_screenshot_shortcut( pub fn close_screenshot_chat_window(app: AppHandle) -> Result<(), String> { info!("关闭截图对话窗口"); - crate::screenshot::window::close_floating_window(&app) - .map_err(|e| format!("关闭窗口失败: {}", e)) + crate::screenshot::window::close_floating_window(&app).map_err(|e| format!("关闭窗口失败: {e}")) } /// 打开带预填文本的输入框 @@ -239,7 +238,7 @@ pub fn open_input_with_text(app: AppHandle, text: String) -> Result<(), String> info!("打开带预填文本的输入框: {} 字符", text.len()); crate::screenshot::window::open_floating_window_with_text(&app, &text) - .map_err(|e| format!("打开窗口失败: {}", e)) + .map_err(|e| format!("打开窗口失败: {e}")) } /// 读取图片文件并转换为 Base64 @@ -267,7 +266,7 @@ pub async fn read_image_as_base64(path: String) -> Result { // 读取文件内容 let bytes = fs::read(path) .await - .map_err(|e| format!("读取文件失败: {}", e))?; + .map_err(|e| format!("读取文件失败: {e}"))?; // 检查文件是否为空 if bytes.is_empty() { @@ -335,7 +334,7 @@ pub async fn send_screenshot_chat( if let Some(main_window) = app.get_webview_window("main") { main_window .emit("smart-input-message", &chat_message) - .map_err(|e| format!("发送事件失败: {}", e))?; + .map_err(|e| format!("发送事件失败: {e}"))?; // 恢复并聚焦主窗口(主窗口在截图时被最小化) let _ = main_window.unminimize(); @@ -344,7 +343,7 @@ pub async fn send_screenshot_chat( } else { // 尝试发送到所有窗口 app.emit("smart-input-message", &chat_message) - .map_err(|e| format!("发送事件失败: {}", e))?; + .map_err(|e| format!("发送事件失败: {e}"))?; } info!("截图对话消息已发送"); diff --git a/src-tauri/src/commands/session_files_cmd.rs b/src-tauri/src/commands/session_files_cmd.rs index f196afe9d..a88340ec3 100644 --- a/src-tauri/src/commands/session_files_cmd.rs +++ b/src-tauri/src/commands/session_files_cmd.rs @@ -21,7 +21,7 @@ pub fn session_files_create( state: State, session_id: String, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.create_session(&session_id) } @@ -31,7 +31,7 @@ pub fn session_files_exists( state: State, session_id: String, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; Ok(storage.session_exists(&session_id)) } @@ -41,7 +41,7 @@ pub fn session_files_get_or_create( state: State, session_id: String, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.get_or_create_session(&session_id) } @@ -51,14 +51,14 @@ pub fn session_files_delete( state: State, session_id: String, ) -> Result<(), String> { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.delete_session(&session_id) } /// 列出所有会话 #[tauri::command] pub fn session_files_list(state: State) -> Result, String> { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.list_sessions() } @@ -68,7 +68,7 @@ pub fn session_files_get_detail( state: State, session_id: String, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.get_session_detail(&session_id) } @@ -81,7 +81,7 @@ pub fn session_files_update_meta( theme: Option, creation_mode: Option, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.update_meta(&session_id, title, theme, creation_mode) } @@ -97,7 +97,7 @@ pub fn session_files_save_file( file_name: String, content: String, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.save_file(&session_id, &file_name, &content) } @@ -108,7 +108,7 @@ pub fn session_files_read_file( session_id: String, file_name: String, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.read_file(&session_id, &file_name) } @@ -119,7 +119,7 @@ pub fn session_files_delete_file( session_id: String, file_name: String, ) -> Result<(), String> { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.delete_file(&session_id, &file_name) } @@ -129,7 +129,7 @@ pub fn session_files_list_files( state: State, session_id: String, ) -> Result, String> { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.list_files(&session_id) } @@ -143,13 +143,13 @@ pub fn session_files_cleanup_expired( state: State, max_age_days: Option, ) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.cleanup_expired(max_age_days.unwrap_or(30)) } /// 清理空会话 #[tauri::command] pub fn session_files_cleanup_empty(state: State) -> Result { - let storage = state.0.lock().map_err(|e| format!("锁定失败: {}", e))?; + let storage = state.0.lock().map_err(|e| format!("锁定失败: {e}"))?; storage.cleanup_empty() } diff --git a/src-tauri/src/commands/skill_cmd.rs b/src-tauri/src/commands/skill_cmd.rs index 2f251e5cd..ee12037b6 100644 --- a/src-tauri/src/commands/skill_cmd.rs +++ b/src-tauri/src/commands/skill_cmd.rs @@ -149,7 +149,7 @@ pub async fn install_skill_for_app( let skill = skills .iter() .find(|s| s.directory == directory) - .ok_or_else(|| format!("Skill not found: {}", directory))?; + .ok_or_else(|| format!("Skill not found: {directory}"))?; let repo_owner = skill .repo_owner diff --git a/src-tauri/src/commands/switch_cmd.rs b/src-tauri/src/commands/switch_cmd.rs index d28386b28..41ceec18d 100644 --- a/src-tauri/src/commands/switch_cmd.rs +++ b/src-tauri/src/commands/switch_cmd.rs @@ -74,7 +74,7 @@ pub fn check_config_sync_status( // 解析 app_type let app_type_enum: AppType = app_type .parse() - .map_err(|e| format!("Invalid app type: {}", e))?; + .map_err(|e| format!("Invalid app type: {e}"))?; // 获取当前 ProxyCast 中设置的 provider let current_provider = SwitchService::get_current_provider(&db, &app_type)? @@ -83,7 +83,7 @@ pub fn check_config_sync_status( // 检查同步状态 check_config_sync(&app_type_enum, ¤t_provider) - .map_err(|e| format!("Failed to check config sync: {}", e)) + .map_err(|e| format!("Failed to check config sync: {e}")) } /// 从外部配置同步到 ProxyCast @@ -95,17 +95,14 @@ pub fn sync_from_external_config( // 解析 app_type let app_type_enum: AppType = app_type .parse() - .map_err(|e| format!("Invalid app type: {}", e))?; + .map_err(|e| format!("Invalid app type: {e}"))?; // 从外部配置获取 provider let external_provider = sync_from_external(&app_type_enum) - .map_err(|e| format!("Failed to sync from external: {}", e))?; + .map_err(|e| format!("Failed to sync from external: {e}"))?; // 切换到外部检测到的 provider SwitchService::switch_provider(&db, &app_type, &external_provider)?; - Ok(format!( - "已同步到外部配置的 provider: {}", - external_provider - )) + Ok(format!("已同步到外部配置的 provider: {external_provider}")) } diff --git a/src-tauri/src/commands/telemetry_cmd.rs b/src-tauri/src/commands/telemetry_cmd.rs index f7f27e0cf..fad60335b 100644 --- a/src-tauri/src/commands/telemetry_cmd.rs +++ b/src-tauri/src/commands/telemetry_cmd.rs @@ -29,8 +29,8 @@ pub struct TelemetryState { impl TelemetryState { /// 创建独立的遥测状态(使用自己的实例) pub fn new() -> Result { - let logger = RequestLogger::with_defaults() - .map_err(|e| format!("Failed to create logger: {}", e))?; + let logger = + RequestLogger::with_defaults().map_err(|e| format!("Failed to create logger: {e}"))?; Ok(Self { logger: Arc::new(logger), @@ -52,7 +52,7 @@ impl TelemetryState { Some(l) => l, None => Arc::new( RequestLogger::with_defaults() - .map_err(|e| format!("Failed to create logger: {}", e))?, + .map_err(|e| format!("Failed to create logger: {e}"))?, ), }; @@ -102,7 +102,7 @@ pub async fn get_request_logs( "timeout" => RequestStatus::Timeout, "retrying" => RequestStatus::Retrying, "cancelled" => RequestStatus::Cancelled, - _ => return Err(format!("Invalid status: {}", s)), + _ => return Err(format!("Invalid status: {s}")), }; logs.retain(|l| l.status == req_status); } @@ -155,7 +155,7 @@ impl TimeRangeParam { "24h" => TimeRange::last_hours(24), "7d" => TimeRange::last_days(7), "30d" => TimeRange::last_days(30), - _ => return Err(format!("Invalid preset: {}", preset)), + _ => return Err(format!("Invalid preset: {preset}")), }; return Ok(Some(range)); } @@ -163,10 +163,10 @@ impl TimeRangeParam { match (&self.start, &self.end) { (Some(s), Some(e)) => { let start = DateTime::parse_from_rfc3339(s) - .map_err(|e| format!("Invalid start time: {}", e))? + .map_err(|e| format!("Invalid start time: {e}"))? .with_timezone(&Utc); let end = DateTime::parse_from_rfc3339(e) - .map_err(|e| format!("Invalid end time: {}", e))? + .map_err(|e| format!("Invalid end time: {e}"))? .with_timezone(&Utc); Ok(Some(TimeRange::new(start, end))) } diff --git a/src-tauri/src/commands/tray_cmd.rs b/src-tauri/src/commands/tray_cmd.rs index 637361cb7..ef70a4f6f 100644 --- a/src-tauri/src/commands/tray_cmd.rs +++ b/src-tauri/src/commands/tray_cmd.rs @@ -92,7 +92,7 @@ pub async fn update_tray_server_status( // 更新服务器相关字段 current_state.server_running = server_running; current_state.server_address = if server_running { - format!("{}:{}", server_host, server_port) + format!("{server_host}:{server_port}") } else { String::new() }; diff --git a/src-tauri/src/commands/unified_chat_cmd.rs b/src-tauri/src/commands/unified_chat_cmd.rs index a27017569..8e0270f84 100644 --- a/src-tauri/src/commands/unified_chat_cmd.rs +++ b/src-tauri/src/commands/unified_chat_cmd.rs @@ -53,14 +53,17 @@ pub struct SendMessageRequest { pub message: String, /// 事件名称(用于前端监听) pub event_name: String, - /// 图片输入(可选) + /// 图片输入(可选,用于多模态对话) + /// TODO: 实现图片处理逻辑,将图片转换为 Aster Message 的 ImageContent pub images: Option>, } /// 图片输入 #[derive(Debug, Deserialize)] pub struct ImageInput { + /// Base64 编码的图片数据 pub data: String, + /// 图片 MIME 类型,如 "image/png", "image/jpeg" pub media_type: String, } @@ -122,8 +125,8 @@ pub async fn chat_create_session( // 保存到数据库 { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; - ChatDao::create_session(&conn, &session).map_err(|e| format!("创建会话失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; + ChatDao::create_session(&conn, &session).map_err(|e| format!("创建会话失败: {e}"))?; } // 初始化 Aster Agent(如果是 Agent 或 Creator 模式) @@ -155,10 +158,10 @@ pub async fn chat_list_sessions( db: State<'_, DbConnection>, mode: Option, ) -> Result, String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let sessions = - ChatDao::list_sessions(&conn, mode).map_err(|e| format!("获取会话列表失败: {}", e))?; + ChatDao::list_sessions(&conn, mode).map_err(|e| format!("获取会话列表失败: {e}"))?; let mut result: Vec = Vec::new(); for session in sessions { @@ -177,10 +180,10 @@ pub async fn chat_get_session( db: State<'_, DbConnection>, session_id: String, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let session = ChatDao::get_session(&conn, &session_id) - .map_err(|e| format!("获取会话失败: {}", e))? + .map_err(|e| format!("获取会话失败: {e}"))? .ok_or_else(|| "会话不存在".to_string())?; let message_count = ChatDao::get_message_count(&conn, &session_id).unwrap_or(0); @@ -196,10 +199,10 @@ pub async fn chat_delete_session( db: State<'_, DbConnection>, session_id: String, ) -> Result { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let deleted = - ChatDao::delete_session(&conn, &session_id).map_err(|e| format!("删除会话失败: {}", e))?; + ChatDao::delete_session(&conn, &session_id).map_err(|e| format!("删除会话失败: {e}"))?; if deleted { tracing::info!("[UnifiedChat] 删除会话: id={}", session_id); @@ -215,10 +218,10 @@ pub async fn chat_rename_session( session_id: String, title: String, ) -> Result<(), String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; ChatDao::update_title(&conn, &session_id, &title) - .map_err(|e| format!("重命名会话失败: {}", e))?; + .map_err(|e| format!("重命名会话失败: {e}"))?; tracing::info!( "[UnifiedChat] 重命名会话: id={}, title={}", @@ -240,10 +243,10 @@ pub async fn chat_get_messages( session_id: String, limit: Option, ) -> Result, String> { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let messages = ChatDao::get_messages(&conn, &session_id, limit) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; Ok(messages) } @@ -258,17 +261,37 @@ pub async fn chat_send_message( agent_state: State<'_, AsterAgentState>, request: SendMessageRequest, ) -> Result<(), String> { + let image_count = request.images.as_ref().map(|v| v.len()).unwrap_or(0); tracing::info!( - "[UnifiedChat] 发送消息: session={}, event={}", + "[UnifiedChat] 发送消息: session={}, event={}, images={}", request.session_id, - request.event_name + request.event_name, + image_count ); + // TODO: 实现图片处理逻辑,将图片转换为 Aster Message 的 ImageContent + if let Some(images) = &request.images { + for (i, img) in images.iter().enumerate() { + tracing::debug!( + "[UnifiedChat] 图片 {}: media_type={}, data_len={}", + i, + img.media_type, + img.data.len() + ); + } + if !images.is_empty() { + tracing::warn!( + "[UnifiedChat] 图片输入暂未实现,忽略 {} 张图片", + images.len() + ); + } + } + // 获取会话信息 let session = { - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; ChatDao::get_session(&conn, &request.session_id) - .map_err(|e| format!("获取会话失败: {}", e))? + .map_err(|e| format!("获取会话失败: {e}"))? .ok_or_else(|| "会话不存在".to_string())? }; @@ -328,7 +351,7 @@ async fn send_message_with_aster( // 构建消息(如果有 system_prompt 且是第一条消息,注入到消息前面) let final_message = if let Some(prompt) = system_prompt { - format!("{}\n\n{}", prompt, message) + format!("{prompt}\n\n{message}") } else { message.to_string() }; @@ -360,7 +383,7 @@ async fn send_message_with_aster( } Err(e) => { let error_event = TauriAgentEvent::Error { - message: format!("流错误: {}", e), + message: format!("流错误: {e}"), }; let _ = app.emit(event_name, &error_event); } @@ -373,10 +396,10 @@ async fn send_message_with_aster( } Err(e) => { let error_event = TauriAgentEvent::Error { - message: format!("Agent 错误: {}", e), + message: format!("Agent 错误: {e}"), }; let _ = app.emit(event_name, &error_event); - return Err(format!("Agent 错误: {}", e)); + return Err(format!("Agent 错误: {e}")); } } diff --git a/src-tauri/src/commands/update_cmd.rs b/src-tauri/src/commands/update_cmd.rs index 11a5b9f6d..e6307b725 100644 --- a/src-tauri/src/commands/update_cmd.rs +++ b/src-tauri/src/commands/update_cmd.rs @@ -67,7 +67,7 @@ pub async fn set_update_check_settings( update_config.show_notification = settings.show_notification; update_config.skipped_version = settings.skipped_version; - config::save_config(&state.config).map_err(|e| format!("保存配置失败: {}", e)) + config::save_config(&state.config).map_err(|e| format!("保存配置失败: {e}")) } /// 跳过指定版本 @@ -80,7 +80,7 @@ pub async fn skip_update_version( let mut state = app_state.write().await; state.config.experimental.update_check.skipped_version = Some(version); - config::save_config(&state.config).map_err(|e| format!("保存配置失败: {}", e))?; + config::save_config(&state.config).map_err(|e| format!("保存配置失败: {e}"))?; // 关闭更新窗口 let _ = update_window::close_update_window(&app_handle); @@ -91,7 +91,7 @@ pub async fn skip_update_version( /// 关闭更新提醒窗口 #[tauri::command] pub fn close_update_window(app_handle: AppHandle) -> Result<(), String> { - update_window::close_update_window(&app_handle).map_err(|e| format!("关闭更新窗口失败: {}", e)) + update_window::close_update_window(&app_handle).map_err(|e| format!("关闭更新窗口失败: {e}")) } /// 测试更新提醒窗口(仅开发环境使用) @@ -102,9 +102,9 @@ pub fn test_update_window(app_handle: AppHandle) -> Result<(), String> { current_version: current_version.to_string(), latest_version: Some("0.99.0".to_string()), has_update: true, - download_url: Some(format!( - "https://github.com/aiclientproxy/proxycast/releases/tag/v0.99.0" - )), + download_url: Some( + "https://github.com/aiclientproxy/proxycast/releases/tag/v0.99.0".to_string(), + ), release_notes_url: None, checked_at: std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -114,7 +114,7 @@ pub fn test_update_window(app_handle: AppHandle) -> Result<(), String> { }; update_window::open_update_window(&app_handle, &test_info) - .map_err(|e| format!("打开更新窗口失败: {}", e)) + .map_err(|e| format!("打开更新窗口失败: {e}")) } /// 更新上次检查时间 @@ -128,7 +128,7 @@ pub async fn update_last_check_timestamp(app_state: State<'_, AppState>) -> Resu let mut state = app_state.write().await; state.config.experimental.update_check.last_check_timestamp = now; - config::save_config(&state.config).map_err(|e| format!("保存配置失败: {}", e))?; + config::save_config(&state.config).map_err(|e| format!("保存配置失败: {e}"))?; Ok(now) } @@ -206,11 +206,10 @@ pub async fn start_background_update_check( // 如果有更新且启用了通知,打开独立的更新提醒窗口 if result.has_update && show_notification { // 检查是否跳过了此版本 - let should_notify = result.latest_version.as_ref().map_or(true, |latest| { - skipped_version - .as_ref() - .map_or(true, |skipped| skipped != latest) - }); + let should_notify = result + .latest_version + .as_ref() + .is_none_or(|latest| skipped_version.as_ref() != Some(latest)); if should_notify { // 打开独立的更新提醒窗口 - 必须在主线程执行 diff --git a/src-tauri/src/commands/usage_cmd.rs b/src-tauri/src/commands/usage_cmd.rs index 701ba438b..f448fb081 100644 --- a/src-tauri/src/commands/usage_cmd.rs +++ b/src-tauri/src/commands/usage_cmd.rs @@ -35,7 +35,7 @@ pub async fn get_kiro_usage( let conn = db.lock().map_err(|e| e.to_string())?; ProviderPoolDao::get_by_uuid(&conn, &credential_uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("凭证不存在: {}", credential_uuid))? + .ok_or_else(|| format!("凭证不存在: {credential_uuid}"))? }; // 2. 验证是否为 Kiro 凭证 @@ -60,7 +60,7 @@ pub async fn get_kiro_usage( .map_err(|e| { // 提供更友好的错误信息 if e.contains("401") || e.contains("Bad credentials") || e.contains("过期") || e.contains("无效") { - format!("刷新 Kiro Token 失败: OAuth 凭证已过期或无效,需要重新认证。\n💡 解决方案:\n1. 删除当前 OAuth 凭证\n2. 重新添加 OAuth 凭证\n3. 确保使用最新的凭证文件\n\n技术详情:{}", e) + format!("刷新 Kiro Token 失败: OAuth 凭证已过期或无效,需要重新认证。\n💡 解决方案:\n1. 删除当前 OAuth 凭证\n2. 重新添加 OAuth 凭证\n3. 确保使用最新的凭证文件\n\n技术详情:{e}") } else { e } @@ -92,11 +92,11 @@ fn read_kiro_credential_info(creds_file_path: &str) -> Result<(String, Option Result { hasher.update(raw_id.as_bytes()); let result = hasher.finalize(); - Ok(format!("{:x}", result)) + Ok(format!("{result:x}")) } /// 获取原始设备 ID @@ -147,7 +147,7 @@ fn get_raw_machine_id() -> Result { let output = Command::new("ioreg") .args(["-rd1", "-c", "IOPlatformExpertDevice"]) .output() - .map_err(|e| format!("执行 ioreg 失败: {}", e))?; + .map_err(|e| format!("执行 ioreg 失败: {e}"))?; let stdout = String::from_utf8_lossy(&output.stdout); for line in stdout.lines() { @@ -225,7 +225,7 @@ mod tests { // 这个测试在不同平台上行为不同 let result = get_machine_id(); // 应该能成功获取 machine_id - assert!(result.is_ok(), "Failed to get machine_id: {:?}", result); + assert!(result.is_ok(), "Failed to get machine_id: {result:?}"); // machine_id 应该是 64 字符的十六进制字符串(SHA256) let id = result.unwrap(); assert_eq!(id.len(), 64, "Machine ID should be 64 hex chars"); diff --git a/src-tauri/src/commands/webview_cmd.rs b/src-tauri/src/commands/webview_cmd.rs index e5bc32e7d..063779731 100644 --- a/src-tauri/src/commands/webview_cmd.rs +++ b/src-tauri/src/commands/webview_cmd.rs @@ -128,7 +128,7 @@ pub async fn create_webview_panel( return Ok(CreateWebviewResponse { success: false, panel_id, - error: Some(format!("无效的 URL: {}", e)), + error: Some(format!("无效的 URL: {e}")), }); } }; @@ -171,7 +171,7 @@ pub async fn create_webview_panel( Ok(CreateWebviewResponse { success: false, panel_id, - error: Some(format!("创建窗口失败: {}", e)), + error: Some(format!("创建窗口失败: {e}")), }) } } @@ -227,13 +227,13 @@ pub async fn navigate_webview_panel( // 解析 URL let parsed_url = url .parse::() - .map_err(|e| format!("无效的 URL: {}", e))?; + .map_err(|e| format!("无效的 URL: {e}"))?; // 获取窗口并导航 if let Some(window) = app.get_webview_window(&panel_id) { // 使用 eval 来导航 - let js = format!("window.location.href = '{}';", parsed_url); - window.eval(&js).map_err(|e| format!("导航失败: {}", e))?; + let js = format!("window.location.href = '{parsed_url}';"); + window.eval(&js).map_err(|e| format!("导航失败: {e}"))?; // 更新状态中的 URL let mut manager = state.0.write().await; @@ -243,7 +243,7 @@ pub async fn navigate_webview_panel( Ok(true) } else { - Err(format!("窗口不存在: {}", panel_id)) + Err(format!("窗口不存在: {panel_id}")) } } @@ -270,7 +270,7 @@ pub async fn resize_webview_panel( // 设置大小 window .set_size(tauri::LogicalSize::new(width, height)) - .map_err(|e| format!("设置大小失败: {}", e))?; + .map_err(|e| format!("设置大小失败: {e}"))?; // 更新状态 let mut manager = state.0.write().await; @@ -281,7 +281,7 @@ pub async fn resize_webview_panel( Ok(true) } else { - Err(format!("窗口不存在: {}", panel_id)) + Err(format!("窗口不存在: {panel_id}")) } } @@ -298,9 +298,9 @@ pub async fn get_webview_panels( #[tauri::command] pub async fn focus_webview_panel(app: AppHandle, panel_id: String) -> Result { if let Some(window) = app.get_webview_window(&panel_id) { - window.set_focus().map_err(|e| format!("聚焦失败: {}", e))?; + window.set_focus().map_err(|e| format!("聚焦失败: {e}"))?; Ok(true) } else { - Err(format!("窗口不存在: {}", panel_id)) + Err(format!("窗口不存在: {panel_id}")) } } diff --git a/src-tauri/src/commands/workspace_cmd.rs b/src-tauri/src/commands/workspace_cmd.rs index 45b74ce19..be59a18cc 100644 --- a/src-tauri/src/commands/workspace_cmd.rs +++ b/src-tauri/src/commands/workspace_cmd.rs @@ -172,7 +172,7 @@ pub async fn workspace_delete( if let Some(workspace) = manager.get(&id)? { let root_path = workspace.root_path; if root_path.exists() && root_path.is_dir() { - std::fs::remove_dir_all(&root_path).map_err(|e| format!("删除目录失败: {}", e))?; + std::fs::remove_dir_all(&root_path).map_err(|e| format!("删除目录失败: {e}"))?; tracing::info!("[Workspace] 删除目录: {:?}", root_path); } } diff --git a/src-tauri/src/config/export.rs b/src-tauri/src/config/export.rs index 6048e7868..675d124bb 100644 --- a/src-tauri/src/config/export.rs +++ b/src-tauri/src/config/export.rs @@ -158,11 +158,11 @@ pub enum ExportError { impl std::fmt::Display for ExportError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ExportError::ConfigError(msg) => write!(f, "配置错误: {}", msg), - ExportError::ReadError(msg) => write!(f, "文件读取错误: {}", msg), - ExportError::SerializeError(msg) => write!(f, "序列化错误: {}", msg), - ExportError::ParseError(msg) => write!(f, "解析错误: {}", msg), - ExportError::TokenFileNotFound(path) => write!(f, "Token 文件不存在: {}", path), + ExportError::ConfigError(msg) => write!(f, "配置错误: {msg}"), + ExportError::ReadError(msg) => write!(f, "文件读取错误: {msg}"), + ExportError::SerializeError(msg) => write!(f, "序列化错误: {msg}"), + ExportError::ParseError(msg) => write!(f, "解析错误: {msg}"), + ExportError::TokenFileNotFound(path) => write!(f, "Token 文件不存在: {path}"), } } } @@ -458,7 +458,7 @@ mod base64 { '0'..='9' => Ok((c as u32) - ('0' as u32) + 52), '+' => Ok(62), '/' => Ok(63), - _ => Err(format!("Invalid base64 character: {}", c)), + _ => Err(format!("Invalid base64 character: {c}")), } }; @@ -726,7 +726,7 @@ mod unit_tests { let original: Vec = (0..len).map(|i| i as u8).collect(); let encoded = base64_encode(&original); let decoded = base64_decode(&encoded).expect("解码应成功"); - assert_eq!(decoded, original, "长度 {} 的数据往返失败", len); + assert_eq!(decoded, original, "长度 {len} 的数据往返失败"); } } diff --git a/src-tauri/src/config/hot_reload.rs b/src-tauri/src/config/hot_reload.rs index 42edeadac..28be048ad 100644 --- a/src-tauri/src/config/hot_reload.rs +++ b/src-tauri/src/config/hot_reload.rs @@ -36,11 +36,11 @@ pub enum HotReloadError { impl std::fmt::Display for HotReloadError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - HotReloadError::WatchError(msg) => write!(f, "文件监控错误: {}", msg), - HotReloadError::LoadError(msg) => write!(f, "配置加载错误: {}", msg), - HotReloadError::ValidationError(msg) => write!(f, "配置验证错误: {}", msg), - HotReloadError::RollbackError(msg) => write!(f, "回滚错误: {}", msg), - HotReloadError::ChannelError(msg) => write!(f, "通道错误: {}", msg), + HotReloadError::WatchError(msg) => write!(f, "文件监控错误: {msg}"), + HotReloadError::LoadError(msg) => write!(f, "配置加载错误: {msg}"), + HotReloadError::ValidationError(msg) => write!(f, "配置验证错误: {msg}"), + HotReloadError::RollbackError(msg) => write!(f, "回滚错误: {msg}"), + HotReloadError::ChannelError(msg) => write!(f, "通道错误: {msg}"), } } } diff --git a/src-tauri/src/config/import.rs b/src-tauri/src/config/import.rs index eb411de85..6954417a3 100644 --- a/src-tauri/src/config/import.rs +++ b/src-tauri/src/config/import.rs @@ -149,12 +149,12 @@ pub enum ImportError { impl std::fmt::Display for ImportError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ImportError::FormatError(msg) => write!(f, "格式错误: {}", msg), - ImportError::VersionError(msg) => write!(f, "版本不兼容: {}", msg), - ImportError::ConfigError(msg) => write!(f, "配置错误: {}", msg), - ImportError::IoError(msg) => write!(f, "IO 错误: {}", msg), - ImportError::ValidationError(msg) => write!(f, "验证错误: {}", msg), - ImportError::RedactedDataError(msg) => write!(f, "脱敏数据无法导入: {}", msg), + ImportError::FormatError(msg) => write!(f, "格式错误: {msg}"), + ImportError::VersionError(msg) => write!(f, "版本不兼容: {msg}"), + ImportError::ConfigError(msg) => write!(f, "配置错误: {msg}"), + ImportError::IoError(msg) => write!(f, "IO 错误: {msg}"), + ImportError::ValidationError(msg) => write!(f, "验证错误: {msg}"), + ImportError::RedactedDataError(msg) => write!(f, "脱敏数据无法导入: {msg}"), } } } @@ -234,7 +234,7 @@ impl ImportService { // 验证配置内容(如果存在) if let Some(ref yaml) = bundle.config_yaml { if let Err(e) = ConfigManager::parse_yaml(yaml) { - result.add_error(format!("配置 YAML 解析失败: {}", e)); + result.add_error(format!("配置 YAML 解析失败: {e}")); } } @@ -453,17 +453,17 @@ impl ImportService { Ok(content) => { // 检查是否是脱敏内容 if content == REDACTED_PLACEHOLDER.as_bytes() { - warnings.push(format!("Token 文件 {} 已脱敏,无法恢复", relative_path)); + warnings.push(format!("Token 文件 {relative_path} 已脱敏,无法恢复")); continue; } // 写入文件 if let Err(e) = std::fs::write(&token_path, &content) { - warnings.push(format!("写入 token 文件 {} 失败: {}", relative_path, e)); + warnings.push(format!("写入 token 文件 {relative_path} 失败: {e}")); } } Err(e) => { - warnings.push(format!("解码 token 文件 {} 失败: {}", relative_path, e)); + warnings.push(format!("解码 token 文件 {relative_path} 失败: {e}")); } } } diff --git a/src-tauri/src/config/tests.rs b/src-tauri/src/config/tests.rs index 55c75ed2d..120cd57a4 100644 --- a/src-tauri/src/config/tests.rs +++ b/src-tauri/src/config/tests.rs @@ -720,7 +720,7 @@ fn arb_tilde_user_path() -> impl Strategy { (username, proptest::collection::vec(path_segment, 0..4)).prop_map(|(user, segments)| { if segments.is_empty() { - format!("~{}", user) + format!("~{user}") } else { format!("~{}/{}", user, segments.join("/")) } @@ -872,7 +872,7 @@ proptest! { #[test] fn prop_collapse_non_home_path_unchanged(path in arb_absolute_path()) { // 确保路径不在主目录下(使用 /tmp 或类似路径) - let test_path = format!("/tmp{}", path); + let test_path = format!("/tmp{path}"); let collapsed = collapse_tilde(&test_path); prop_assert_eq!( @@ -892,7 +892,7 @@ proptest! { /// 生成有效的 YAML 注释(以 # 开头) fn arb_yaml_comment() -> impl Strategy { // 生成注释内容:字母、数字、空格、中文字符 - "[a-zA-Z0-9 ]{1,50}".prop_map(|s| format!("# {}", s)) + "[a-zA-Z0-9 ]{1,50}".prop_map(|s| format!("# {s}")) } /// 生成带注释的 YAML 配置字符串 @@ -1039,7 +1039,7 @@ proptest! { // 创建带头部注释的 YAML let yaml = ConfigManager::to_yaml(&config).expect("序列化应成功"); - let yaml_with_header = format!("{}\n{}", header_comment, yaml); + let yaml_with_header = format!("{header_comment}\n{yaml}"); // 写入文件 std::fs::write(&config_path, &yaml_with_header).expect("写入文件失败"); diff --git a/src-tauri/src/config/types.rs b/src-tauri/src/config/types.rs index bea57321f..ccc0fe278 100644 --- a/src-tauri/src/config/types.rs +++ b/src-tauri/src/config/types.rs @@ -125,7 +125,7 @@ fn default_asr_language() -> String { } /// Whisper 本地配置 -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] pub struct WhisperLocalConfig { /// 模型大小 #[serde(default)] @@ -135,15 +135,6 @@ pub struct WhisperLocalConfig { pub model_path: Option, } -impl Default for WhisperLocalConfig { - fn default() -> Self { - Self { - model: WhisperModelSize::default(), - model_path: None, - } - } -} - /// 讯飞语音配置 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct XunfeiConfig { diff --git a/src-tauri/src/config/yaml.rs b/src-tauri/src/config/yaml.rs index 0190c6c53..0979d3d28 100644 --- a/src-tauri/src/config/yaml.rs +++ b/src-tauri/src/config/yaml.rs @@ -27,11 +27,11 @@ pub enum ConfigError { impl std::fmt::Display for ConfigError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ConfigError::ReadError(msg) => write!(f, "配置读取错误: {}", msg), - ConfigError::WriteError(msg) => write!(f, "配置写入错误: {}", msg), - ConfigError::ParseError(msg) => write!(f, "YAML 解析错误: {}", msg), - ConfigError::SerializeError(msg) => write!(f, "YAML 序列化错误: {}", msg), - ConfigError::ValidationError(msg) => write!(f, "配置验证错误: {}", msg), + ConfigError::ReadError(msg) => write!(f, "配置读取错误: {msg}"), + ConfigError::WriteError(msg) => write!(f, "配置写入错误: {msg}"), + ConfigError::ParseError(msg) => write!(f, "YAML 解析错误: {msg}"), + ConfigError::SerializeError(msg) => write!(f, "YAML 序列化错误: {msg}"), + ConfigError::ValidationError(msg) => write!(f, "配置验证错误: {msg}"), } } } diff --git a/src-tauri/src/connect/deep_link.rs b/src-tauri/src/connect/deep_link.rs index e662155a8..351fe3450 100644 --- a/src-tauri/src/connect/deep_link.rs +++ b/src-tauri/src/connect/deep_link.rs @@ -55,7 +55,7 @@ pub enum DeepLinkError { impl std::fmt::Display for DeepLinkError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - DeepLinkError::InvalidUrl(msg) => write!(f, "无效的 URL: {}", msg), + DeepLinkError::InvalidUrl(msg) => write!(f, "无效的 URL: {msg}"), DeepLinkError::MissingRelay => write!(f, "缺少必填参数: relay"), DeepLinkError::MissingKey => write!(f, "缺少必填参数: key"), } @@ -273,12 +273,12 @@ mod property_tests { ref_code in arb_ref_code(), ) { // 构建 URL - let mut url = format!("proxycast://connect?relay={}&key={}", relay, key); + let mut url = format!("proxycast://connect?relay={relay}&key={key}"); if let Some(ref n) = name { url.push_str(&format!("&name={}", urlencoding::encode(n))); } if let Some(ref r) = ref_code { - url.push_str(&format!("&ref={}", r)); + url.push_str(&format!("&ref={r}")); } // 解析 URL @@ -310,13 +310,13 @@ mod property_tests { ) { let url = match error_type { // 缺少 relay 参数 - 0 => format!("proxycast://connect?key={}", key), + 0 => format!("proxycast://connect?key={key}"), // 缺少 key 参数 - 1 => format!("proxycast://connect?relay={}", relay), + 1 => format!("proxycast://connect?relay={relay}"), // 空 relay 参数 - 2 => format!("proxycast://connect?relay=&key={}", key), + 2 => format!("proxycast://connect?relay=&key={key}"), // 空 key 参数 - _ => format!("proxycast://connect?relay={}&key=", relay), + _ => format!("proxycast://connect?relay={relay}&key="), }; let result = parse_deep_link(&url); @@ -345,7 +345,7 @@ mod property_tests { relay in arb_relay_id(), key in arb_api_key(), ) { - let url = format!("{}://connect?relay={}&key={}", protocol, relay, key); + let url = format!("{protocol}://connect?relay={relay}&key={key}"); let result = parse_deep_link(&url); prop_assert!( @@ -361,7 +361,7 @@ mod property_tests { relay in arb_relay_id(), key in arb_api_key(), ) { - let url = format!("proxycast://{}?relay={}&key={}", path, relay, key); + let url = format!("proxycast://{path}?relay={relay}&key={key}"); let result = parse_deep_link(&url); prop_assert!( diff --git a/src-tauri/src/connect/registry.rs b/src-tauri/src/connect/registry.rs index 927d94130..0fe616920 100644 --- a/src-tauri/src/connect/registry.rs +++ b/src-tauri/src/connect/registry.rs @@ -407,26 +407,26 @@ mod tests { RelayInfo { id: id.to_string(), name: name.to_string(), - description: format!("{} 描述", name), + description: format!("{name} 描述"), branding: RelayBranding { - logo: format!("https://example.com/{}/logo.png", id), + logo: format!("https://example.com/{id}/logo.png"), color: "#6366f1".to_string(), }, links: RelayLinks { - homepage: format!("https://{}.example.com", id), - register: Some(format!("https://{}.example.com/register", id)), + homepage: format!("https://{id}.example.com"), + register: Some(format!("https://{id}.example.com/register")), recharge: None, - docs: Some(format!("https://docs.{}.example.com", id)), + docs: Some(format!("https://docs.{id}.example.com")), status: None, }, api: RelayApi { - base_url: format!("https://api.{}.example.com/v1", id), + base_url: format!("https://api.{id}.example.com/v1"), protocol: "openai".to_string(), auth_header: "Authorization".to_string(), auth_prefix: "Bearer".to_string(), }, contact: RelayContact { - email: Some(format!("support@{}.example.com", id)), + email: Some(format!("support@{id}.example.com")), discord: None, telegram: None, twitter: None, @@ -610,20 +610,20 @@ mod property_tests { (arb_relay_id(), arb_relay_name()).prop_map(|(id, name)| RelayInfo { id: id.clone(), name: name.clone(), - description: format!("{} 描述", name), + description: format!("{name} 描述"), branding: RelayBranding { - logo: format!("https://example.com/{}/logo.png", id), + logo: format!("https://example.com/{id}/logo.png"), color: "#6366f1".to_string(), }, links: RelayLinks { - homepage: format!("https://{}.example.com", id), + homepage: format!("https://{id}.example.com"), register: None, recharge: None, docs: None, status: None, }, api: RelayApi { - base_url: format!("https://api.{}.example.com/v1", id), + base_url: format!("https://api.{id}.example.com/v1"), protocol: "openai".to_string(), auth_header: "Authorization".to_string(), auth_prefix: "Bearer".to_string(), diff --git a/src-tauri/src/content/manager.rs b/src-tauri/src/content/manager.rs index 1e3d162a7..f378a5cc3 100644 --- a/src-tauri/src/content/manager.rs +++ b/src-tauri/src/content/manager.rs @@ -61,10 +61,7 @@ impl ContentManager { updated_at: now, }; - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; conn.execute( "INSERT INTO contents (id, project_id, title, content_type, status, sort_order, body, word_count, metadata_json, session_id, created_at, updated_at) @@ -84,7 +81,7 @@ impl ContentManager { content.updated_at.timestamp_millis(), ], ) - .map_err(|e| format!("创建内容失败: {}", e))?; + .map_err(|e| format!("创建内容失败: {e}"))?; tracing::info!( "[Content] 创建: id={}, project_id={}, title={}", @@ -131,22 +128,19 @@ impl ContentManager { /// 获取内容 pub fn get(&self, id: &ContentId) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT id, project_id, title, content_type, status, sort_order, body, word_count, metadata_json, session_id, created_at, updated_at FROM contents WHERE id = ?", params![id], - |row| Ok(Self::row_to_content(row)?), + Self::row_to_content, ); match result { Ok(content) => Ok(Some(content)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取内容失败: {}", e)), + Err(e) => Err(format!("获取内容失败: {e}")), } } @@ -156,10 +150,7 @@ impl ContentManager { project_id: &str, query: Option, ) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let query = query.unwrap_or_default(); @@ -185,7 +176,7 @@ impl ContentManager { // 搜索 if let Some(ref search) = query.search { sql.push_str(" AND (title LIKE ? OR body LIKE ?)"); - let search_pattern = format!("%{}%", search); + let search_pattern = format!("%{search}%"); params_vec.push(Box::new(search_pattern.clone())); params_vec.push(Box::new(search_pattern)); } @@ -193,7 +184,7 @@ impl ContentManager { // 排序 let sort_by = query.sort_by.unwrap_or_else(|| "sort_order".to_string()); let sort_order = query.sort_order.unwrap_or_else(|| "asc".to_string()); - sql.push_str(&format!(" ORDER BY {} {}", sort_by, sort_order)); + sql.push_str(&format!(" ORDER BY {sort_by} {sort_order}")); // 分页 if let Some(limit) = query.limit { @@ -210,23 +201,20 @@ impl ContentManager { let mut stmt = conn .prepare(&sql) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let contents = stmt - .query_map(params_refs.as_slice(), |row| Ok(Self::row_to_content(row)?)) - .map_err(|e| format!("查询失败: {}", e))? + .query_map(params_refs.as_slice(), Self::row_to_content) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(contents) } /// 更新内容 pub fn update(&self, id: &ContentId, updates: ContentUpdateRequest) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let now = Utc::now().timestamp_millis(); // 构建更新语句 @@ -277,7 +265,7 @@ impl ContentManager { params_vec.iter().map(|p| p.as_ref()).collect(); conn.execute(&sql, params_refs.as_slice()) - .map_err(|e| format!("更新内容失败: {}", e))?; + .map_err(|e| format!("更新内容失败: {e}"))?; drop(conn); @@ -286,14 +274,11 @@ impl ContentManager { /// 删除内容 pub fn delete(&self, id: &ContentId) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let affected = conn .execute("DELETE FROM contents WHERE id = ?", params![id]) - .map_err(|e| format!("删除内容失败: {}", e))?; + .map_err(|e| format!("删除内容失败: {e}"))?; if affected > 0 { tracing::info!("[Content] 删除: id={}", id); @@ -304,17 +289,14 @@ impl ContentManager { /// 批量删除项目下的所有内容 pub fn delete_by_project(&self, project_id: &str) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let affected = conn .execute( "DELETE FROM contents WHERE project_id = ?", params![project_id], ) - .map_err(|e| format!("删除内容失败: {}", e))?; + .map_err(|e| format!("删除内容失败: {e}"))?; tracing::info!( "[Content] 批量删除: project_id={}, count={}", @@ -327,10 +309,7 @@ impl ContentManager { /// 获取项目的内容统计 pub fn get_project_stats(&self, project_id: &str) -> Result<(i64, i64, i64), String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT COUNT(*), COALESCE(SUM(word_count), 0), COUNT(CASE WHEN status = 'completed' THEN 1 END) @@ -346,16 +325,13 @@ impl ContentManager { match result { Ok(stats) => Ok(stats), - Err(e) => Err(format!("获取统计失败: {}", e)), + Err(e) => Err(format!("获取统计失败: {e}")), } } /// 重新排序内容 pub fn reorder(&self, project_id: &str, content_ids: Vec) -> Result<(), String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; for (index, content_id) in content_ids.iter().enumerate() { conn.execute( @@ -367,7 +343,7 @@ impl ContentManager { project_id ], ) - .map_err(|e| format!("重新排序失败: {}", e))?; + .map_err(|e| format!("重新排序失败: {e}"))?; } Ok(()) @@ -375,10 +351,7 @@ impl ContentManager { /// 获取下一个排序顺序 fn get_next_order(&self, project_id: &str) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result: Result = conn.query_row( "SELECT COALESCE(MAX(sort_order), -1) + 1 FROM contents WHERE project_id = ?", @@ -386,7 +359,7 @@ impl ContentManager { |row| row.get(0), ); - result.map_err(|e| format!("获取排序顺序失败: {}", e)) + result.map_err(|e| format!("获取排序顺序失败: {e}")) } /// 从数据库行解析 Content diff --git a/src-tauri/src/converter/openai_to_antigravity.rs b/src-tauri/src/converter/openai_to_antigravity.rs index fdf122c96..a128b44bd 100644 --- a/src-tauri/src/converter/openai_to_antigravity.rs +++ b/src-tauri/src/converter/openai_to_antigravity.rs @@ -187,7 +187,7 @@ fn generate_random_session_id() -> String { let n: u64 = u64::from_le_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], ]) % 9_000_000_000_000_000_000; - format!("-{}", n) + format!("-{n}") } /// 获取默认安全设置 @@ -292,15 +292,15 @@ pub fn convert_openai_to_antigravity_with_context( ) -> serde_json::Value { eprintln!("========== [CONVERT] OpenAI -> Antigravity 转换开始 =========="); eprintln!("[CONVERT] 原始模型: {}", request.model); - eprintln!("[CONVERT] 项目ID: {}", project_id); + eprintln!("[CONVERT] 项目ID: {project_id}"); eprintln!("[CONVERT] 消息数量: {}", request.messages.len()); eprintln!("[CONVERT] 流式: {}", request.stream); let actual_model = model_mapping(&request.model); - eprintln!("[CONVERT] 映射后模型: {}", actual_model); + eprintln!("[CONVERT] 映射后模型: {actual_model}"); let supports_thinking = model_supports_thinking(actual_model); - eprintln!("[CONVERT] 支持思维链: {}", supports_thinking); + eprintln!("[CONVERT] 支持思维链: {supports_thinking}"); let mut contents: Vec = Vec::new(); let mut system_instruction: Option = None; @@ -575,15 +575,9 @@ pub fn convert_openai_to_antigravity_with_context( "[ANTIGRAVITY] 图片生成模型 {} 已启用 IMAGE 响应模态", actual_model ); - eprintln!( - "[ANTIGRAVITY] 图片生成模型 {} 已启用 IMAGE 响应模态", - actual_model - ); + eprintln!("[ANTIGRAVITY] 图片生成模型 {actual_model} 已启用 IMAGE 响应模态"); } else { - eprintln!( - "[ANTIGRAVITY] 模型 {} 不是图片生成模型,不启用 IMAGE 响应模态", - actual_model - ); + eprintln!("[ANTIGRAVITY] 模型 {actual_model} 不是图片生成模型,不启用 IMAGE 响应模态"); } // 处理 reasoning_effort(思维链配置) @@ -690,7 +684,7 @@ pub fn convert_openai_to_antigravity_with_context( // 使用 SessionManager 生成稳定的会话 ID let session_id = SessionManager::extract_session_id(request); - eprintln!("[CONVERT] 生成的稳定 SessionId: {}", session_id); + eprintln!("[CONVERT] 生成的稳定 SessionId: {session_id}"); let inner = AntigravityRequestInner { contents, @@ -950,11 +944,11 @@ pub fn convert_antigravity_to_openai_response( .unwrap_or("image/png"); // 将图片作为 data URL 添加到内容中 - let image_url = format!("data:{};base64,{}", mime_type, data); + let image_url = format!("data:{mime_type};base64,{data}"); if !content.is_empty() { content.push_str("\n\n"); } - content.push_str(&format!("![image]({})", image_url)); + content.push_str(&format!("![image]({image_url})")); } } } @@ -1181,7 +1175,7 @@ pub fn convert_antigravity_image_response( } } else { // 构建 data URL - let data_url = format!("data:{};base64,{}", mime_type, data); + let data_url = format!("data:{mime_type};base64,{data}"); ImageData { b64_json: None, url: Some(data_url), @@ -1532,7 +1526,7 @@ mod image_property_tests { let result = convert_antigravity_image_response(&antigravity_resp, &response_format).unwrap(); - prop_assert!(result.data.len() >= 1); + prop_assert!(!result.data.is_empty()); if response_format == "b64_json" { // b64_json 格式 @@ -1546,7 +1540,7 @@ mod image_property_tests { // 验证 data URL 格式 let url = result.data[0].url.as_ref().unwrap(); - let expected_url = format!("data:{};base64,{}", mime_type, base64_data); + let expected_url = format!("data:{mime_type};base64,{base64_data}"); prop_assert_eq!(url, &expected_url); } } diff --git a/src-tauri/src/converter/openai_to_cw.rs b/src-tauri/src/converter/openai_to_cw.rs index 5886c5325..b6183fca7 100644 --- a/src-tauri/src/converter/openai_to_cw.rs +++ b/src-tauri/src/converter/openai_to_cw.rs @@ -345,7 +345,7 @@ pub fn convert_openai_to_codewhisperer( // P1 安全修复:使用字符边界安全的截断,防止 UTF-8 panic description: if desc.len() > 500 { let truncated: String = desc.chars().take(497).collect(); - format!("{}...", truncated) + format!("{truncated}...") } else { desc }, diff --git a/src-tauri/src/converter/reasoning_handler.rs b/src-tauri/src/converter/reasoning_handler.rs index a47607c80..4eebdf735 100644 --- a/src-tauri/src/converter/reasoning_handler.rs +++ b/src-tauri/src/converter/reasoning_handler.rs @@ -16,6 +16,13 @@ //! - 只有 `content` 字段需要保留在对话历史中 //! - Tool Calls 场景下,需要正确处理 reasoning_content 的传递 //! +//! # 使用状态 +//! +//! 此模块为预留功能,将在 Proxy 层集成推理模型时启用。 +//! 目前代码已完成,等待在 `proxy_handler.rs` 中调用 `ReasoningHandler::preprocess_messages`。 + +// 预留功能模块,暂未在主流程中调用 +#![allow(dead_code)] use crate::models::openai::ChatMessage; @@ -85,8 +92,6 @@ impl ReasoningHandler { /// /// 清除历史消息中的 reasoning_content,只保留最后一条 assistant 消息的 reasoning_content fn process_deepseek_messages(mut messages: Vec) -> Vec { - let len = messages.len(); - // 先找出最后一条 assistant 消息的索引 let last_assistant_idx = messages .iter() diff --git a/src-tauri/src/credential/balancer.rs b/src-tauri/src/credential/balancer.rs index e9642672f..704ee8a50 100644 --- a/src-tauri/src/credential/balancer.rs +++ b/src-tauri/src/credential/balancer.rs @@ -181,7 +181,7 @@ impl LoadBalancer { let client = self .proxy_factory .create_client(credential.proxy_url()) - .map_err(|e| PoolError::CredentialNotFound(format!("代理配置错误: {}", e)))?; + .map_err(|e| PoolError::CredentialNotFound(format!("代理配置错误: {e}")))?; Ok(CredentialSelection { credential, client }) } @@ -200,7 +200,7 @@ impl LoadBalancer { ) -> Result { self.proxy_factory .create_client(credential.proxy_url()) - .map_err(|e| PoolError::CredentialNotFound(format!("代理配置错误: {}", e))) + .map_err(|e| PoolError::CredentialNotFound(format!("代理配置错误: {e}"))) } /// 选择下一个可用凭证,支持代理失败时的故障转移 @@ -468,7 +468,7 @@ mod balancer_tests { id.to_string(), provider, CredentialData::ApiKey { - key: format!("key-{}", id), + key: format!("key-{id}"), base_url: None, }, ) diff --git a/src-tauri/src/credential/health.rs b/src-tauri/src/credential/health.rs index 99e057a72..64669d172 100644 --- a/src-tauri/src/credential/health.rs +++ b/src-tauri/src/credential/health.rs @@ -253,7 +253,7 @@ mod health_tests { id.to_string(), ProviderType::Kiro, CredentialData::ApiKey { - key: format!("key-{}", id), + key: format!("key-{id}"), base_url: None, }, ) diff --git a/src-tauri/src/credential/pool.rs b/src-tauri/src/credential/pool.rs index 379bb88d3..b3283ae46 100644 --- a/src-tauri/src/credential/pool.rs +++ b/src-tauri/src/credential/pool.rs @@ -52,8 +52,8 @@ pub enum PoolError { impl std::fmt::Display for PoolError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PoolError::CredentialExists(id) => write!(f, "凭证已存在: {}", id), - PoolError::CredentialNotFound(id) => write!(f, "凭证不存在: {}", id), + PoolError::CredentialExists(id) => write!(f, "凭证已存在: {id}"), + PoolError::CredentialNotFound(id) => write!(f, "凭证不存在: {id}"), PoolError::EmptyPool => write!(f, "凭证池为空"), PoolError::NoAvailableCredential => write!(f, "没有可用的凭证"), } @@ -291,7 +291,7 @@ mod pool_tests { id.to_string(), ProviderType::Kiro, CredentialData::ApiKey { - key: format!("key-{}", id), + key: format!("key-{id}"), base_url: None, }, ) diff --git a/src-tauri/src/credential/quota.rs b/src-tauri/src/credential/quota.rs index 7ef410544..bc635610d 100644 --- a/src-tauri/src/credential/quota.rs +++ b/src-tauri/src/credential/quota.rs @@ -262,7 +262,7 @@ impl QuotaManager { } // 添加 -preview 后缀 - Some(format!("{}-preview", model)) + Some(format!("{model}-preview")) } /// 检查模型是否为预览版本 @@ -391,7 +391,7 @@ pub struct QuotaAutoSwitchResult { impl QuotaAutoSwitchResult { /// 创建成功切换的结果 pub fn switched(new_credential_id: String) -> Self { - let message = format!("已切换到凭证: {}", new_credential_id); + let message = format!("已切换到凭证: {new_credential_id}"); Self { switched: true, new_credential_id: Some(new_credential_id), @@ -403,7 +403,7 @@ impl QuotaAutoSwitchResult { /// 创建使用预览模型的结果 pub fn preview_model(model: String) -> Self { - let message = format!("已切换到预览模型: {}", model); + let message = format!("已切换到预览模型: {model}"); Self { switched: false, new_credential_id: None, @@ -427,7 +427,7 @@ impl QuotaAutoSwitchResult { /// 创建所有凭证耗尽的结果 pub fn all_exhausted(earliest_recovery: Option>) -> Self { let message = match earliest_recovery { - Some(time) => format!("所有凭证配额超限,最早恢复时间: {}", time), + Some(time) => format!("所有凭证配额超限,最早恢复时间: {time}"), None => "所有凭证配额超限,无可用凭证".to_string(), }; Self { diff --git a/src-tauri/src/credential/risk.rs b/src-tauri/src/credential/risk.rs index cd580dbea..c5e433dbe 100644 --- a/src-tauri/src/credential/risk.rs +++ b/src-tauri/src/credential/risk.rs @@ -478,7 +478,7 @@ mod tests { for i in 0..5 { let event = RateLimitEvent::new("cred-1".to_string()) .with_status_code(429) - .with_error_message(format!("Rate limit {}", i)); + .with_error_message(format!("Rate limit {i}")); controller.record_rate_limit(event); } diff --git a/src-tauri/src/credential/sync.rs b/src-tauri/src/credential/sync.rs index bb1b3ac96..c54add21b 100644 --- a/src-tauri/src/credential/sync.rs +++ b/src-tauri/src/credential/sync.rs @@ -26,10 +26,10 @@ pub enum SyncError { impl std::fmt::Display for SyncError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - SyncError::ConfigError(msg) => write!(f, "配置错误: {}", msg), - SyncError::IoError(msg) => write!(f, "IO 错误: {}", msg), - SyncError::CredentialNotFound(id) => write!(f, "凭证不存在: {}", id), - SyncError::InvalidCredentialType(msg) => write!(f, "无效的凭证类型: {}", msg), + SyncError::ConfigError(msg) => write!(f, "配置错误: {msg}"), + SyncError::IoError(msg) => write!(f, "IO 错误: {msg}"), + SyncError::CredentialNotFound(id) => write!(f, "凭证不存在: {id}"), + SyncError::InvalidCredentialType(msg) => write!(f, "无效的凭证类型: {msg}"), } } } @@ -67,7 +67,7 @@ impl CredentialSyncService { let manager = self .config_manager .read() - .map_err(|e| SyncError::ConfigError(format!("获取配置锁失败: {}", e)))?; + .map_err(|e| SyncError::ConfigError(format!("获取配置锁失败: {e}")))?; Ok(manager.config().clone()) } @@ -76,7 +76,7 @@ impl CredentialSyncService { let mut manager = self .config_manager .write() - .map_err(|e| SyncError::ConfigError(format!("获取配置写锁失败: {}", e)))?; + .map_err(|e| SyncError::ConfigError(format!("获取配置写锁失败: {e}")))?; let config_path = manager.config_path().to_path_buf(); manager.set_config(config.clone()); @@ -252,7 +252,7 @@ impl CredentialSyncService { std::fs::create_dir_all(&provider_dir)?; // 生成 token 文件名 - let token_filename = format!("{}.json", credential_id); + let token_filename = format!("{credential_id}.json"); let token_path = provider_dir.join(&token_filename); // 展开源路径并复制文件 @@ -262,7 +262,7 @@ impl CredentialSyncService { } // 返回相对路径 - Ok(format!("{}/{}", provider, token_filename)) + Ok(format!("{provider}/{token_filename}")) } /// 删除凭证并同步到配置 diff --git a/src-tauri/src/credential/tests.rs b/src-tauri/src/credential/tests.rs index 64a2175de..0d8bfadca 100644 --- a/src-tauri/src/credential/tests.rs +++ b/src-tauri/src/credential/tests.rs @@ -235,7 +235,7 @@ fn arb_unique_credentials_same_provider( data_list .into_iter() .enumerate() - .map(|(i, data)| Credential::new(format!("cred-{}", i), provider, data)) + .map(|(i, data)| Credential::new(format!("cred-{i}"), provider, data)) .collect() }) } @@ -256,10 +256,10 @@ proptest! { // 添加 N 个凭证 for i in 0..cred_count { let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, ); @@ -303,10 +303,10 @@ proptest! { // 添加 N 个凭证 for i in 0..cred_count { let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, ); @@ -550,10 +550,10 @@ proptest! { let cred_count = 5usize; for i in 0..cred_count { let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, ); @@ -562,7 +562,7 @@ proptest! { lb.register_pool(pool.clone()); - let cooldown_id = format!("cred-{}", cooldown_index); + let cooldown_id = format!("cred-{cooldown_index}"); // 标记一个凭证为冷却状态(1小时后恢复) lb.mark_cooldown(provider, &cooldown_id, Duration::hours(1)).unwrap(); @@ -631,10 +631,10 @@ proptest! { // 添加凭证 for i in 0..cred_count { let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, ); @@ -645,7 +645,7 @@ proptest! { // 将所有凭证标记为冷却 for i in 0..cred_count { - lb.mark_cooldown(provider, &format!("cred-{}", i), Duration::hours(1)) + lb.mark_cooldown(provider, &format!("cred-{i}"), Duration::hours(1)) .unwrap(); } @@ -818,12 +818,12 @@ proptest! { for i in 0..cred_count { let cred_data = if i % 2 == 0 { PoolCredentialData::OpenAIKey { - api_key: format!("sk-test-key-{}", i), + api_key: format!("sk-test-key-{i}"), base_url: Some("https://api.openai.com/v1".to_string()), } } else { PoolCredentialData::ClaudeKey { - api_key: format!("sk-ant-test-key-{}", i), + api_key: format!("sk-ant-test-key-{i}"), base_url: None, } }; @@ -879,7 +879,7 @@ proptest! { std::fs::create_dir_all(&source_token_dir).expect("创建源目录失败"); let source_token_path = source_token_dir.join("token.json"); - let token_json = format!(r#"{{"access_token": "{}", "refresh_token": "refresh-{}", "expires_at": "2025-12-31T23:59:59Z"}}"#, token_content, token_content); + let token_json = format!(r#"{{"access_token": "{token_content}", "refresh_token": "refresh-{token_content}", "expires_at": "2025-12-31T23:59:59Z"}}"#); std::fs::write(&source_token_path, &token_json).expect("写入源 token 文件失败"); // 根据索引选择 provider 类型 @@ -920,7 +920,7 @@ proptest! { PoolProviderType::Gemini => "gemini", _ => "unknown", }; - let expected_token_path = auth_dir.join(provider_name).join(format!("{}.json", original_uuid)); + let expected_token_path = auth_dir.join(provider_name).join(format!("{original_uuid}.json")); prop_assert!( expected_token_path.exists(), @@ -983,7 +983,7 @@ proptest! { std::fs::create_dir_all(&source_token_dir).expect("创建源目录失败"); let source_token_path = source_token_dir.join("token.json"); - let initial_json = format!(r#"{{"access_token": "{}"}}"#, initial_content); + let initial_json = format!(r#"{{"access_token": "{initial_content}"}}"#); std::fs::write(&source_token_path, &initial_json).expect("写入初始 token 文件失败"); // 创建凭证 @@ -999,7 +999,7 @@ proptest! { sync_service.add_credential(&credential).expect("添加凭证失败"); // 更新源 token 文件内容 - let updated_json = format!(r#"{{"access_token": "{}"}}"#, updated_content); + let updated_json = format!(r#"{{"access_token": "{updated_content}"}}"#); std::fs::write(&source_token_path, &updated_json).expect("更新源 token 文件失败"); // 更新凭证 @@ -1013,7 +1013,7 @@ proptest! { // 验证 auth_dir 中的 token 文件已更新 let auth_dir = sync_service.get_auth_dir().expect("获取 auth_dir 失败"); - let token_path = auth_dir.join("kiro").join(format!("{}.json", original_uuid)); + let token_path = auth_dir.join("kiro").join(format!("{original_uuid}.json")); let stored_content = std::fs::read_to_string(&token_path) .expect("读取存储的 token 文件失败"); @@ -1040,8 +1040,8 @@ proptest! { per_key_proxy in "[a-z0-9]{1,10}", global_proxy in "[a-z0-9]{1,10}" ) { - let per_key_url = format!("http://{}:8080", per_key_proxy); - let global_url = format!("http://{}:8080", global_proxy); + let per_key_url = format!("http://{per_key_proxy}:8080"); + let global_url = format!("http://{global_proxy}:8080"); let lb = LoadBalancer::new(BalanceStrategy::RoundRobin) .with_global_proxy(Some(global_url.clone())); @@ -1155,7 +1155,7 @@ proptest! { // Hostname must start with a letter to be valid proxy_host in "[a-z][a-z0-9]{0,9}" ) { - let proxy_url = format!("http://{}:8080", proxy_host); + let proxy_url = format!("http://{proxy_host}:8080"); let lb = LoadBalancer::new(BalanceStrategy::RoundRobin); let pool = Arc::new(CredentialPool::new(provider)); @@ -1210,14 +1210,14 @@ proptest! { Some("ftp://invalid-proxy:21".to_string()) } else { // 其他凭证使用有效代理 - Some(format!("http://valid-proxy-{}:8080", i)) + Some(format!("http://valid-proxy-{i}:8080")) }; let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, ).with_proxy(proxy_url); @@ -1255,13 +1255,13 @@ proptest! { // 创建多个凭证,都有有效代理 for i in 0..cred_count { let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, - ).with_proxy(Some(format!("http://proxy-{}:8080", i))); + ).with_proxy(Some(format!("http://proxy-{i}:8080"))); pool.add(cred).unwrap(); } @@ -1288,13 +1288,13 @@ proptest! { // 创建多个凭证,都有无效代理 for i in 0..cred_count { let cred = Credential::new( - format!("cred-{}", i), + format!("cred-{i}"), provider, CredentialData::ApiKey { - key: format!("key-{}", i), + key: format!("key-{i}"), base_url: None, }, - ).with_proxy(Some(format!("ftp://invalid-proxy-{}:21", i))); + ).with_proxy(Some(format!("ftp://invalid-proxy-{i}:21"))); pool.add(cred).unwrap(); } @@ -1545,7 +1545,7 @@ proptest! { // 标记多个凭证为配额超限 let mut marked_ids = Vec::new(); for i in 0..cred_count { - let cred_id = format!("cred-{}", i); + let cred_id = format!("cred-{i}"); manager.mark_quota_exceeded(&cred_id, "Rate limit exceeded"); marked_ids.push(cred_id); } @@ -1596,7 +1596,7 @@ proptest! { // 创建凭证 ID 列表 let available: Vec = (0..cred_count) - .map(|i| format!("cred-{}", i)) + .map(|i| format!("cred-{i}")) .collect(); let failed_index = failed_index % cred_count; @@ -1656,7 +1656,7 @@ proptest! { // 创建凭证 ID 列表 let available: Vec = (0..cred_count) - .map(|i| format!("cred-{}", i)) + .map(|i| format!("cred-{i}")) .collect(); let failed_index = failed_index % cred_count; @@ -1701,7 +1701,7 @@ proptest! { // 创建凭证 ID 列表 let available: Vec = (0..cred_count) - .map(|i| format!("cred-{}", i)) + .map(|i| format!("cred-{i}")) .collect(); // 标记所有凭证为配额超限 @@ -1753,7 +1753,7 @@ proptest! { // 标记多个凭证为配额超限 let cred_ids: Vec = (0..cred_count) - .map(|i| format!("cred-{}", i)) + .map(|i| format!("cred-{i}")) .collect(); for cred_id in &cred_ids { @@ -1815,7 +1815,7 @@ proptest! { // 标记多个凭证为配额超限 let cred_ids: Vec = (0..cred_count) - .map(|i| format!("cred-{}", i)) + .map(|i| format!("cred-{i}")) .collect(); for cred_id in &cred_ids { @@ -1875,12 +1875,12 @@ proptest! { // 标记一些凭证为立即过期 let expired_ids: Vec = (0..expired_count) - .map(|i| format!("expired-{}", i)) + .map(|i| format!("expired-{i}")) .collect(); // 标记一些凭证为长时间冷却 let active_ids: Vec = (0..active_count) - .map(|i| format!("active-{}", i)) + .map(|i| format!("active-{i}")) .collect(); // 先标记所有凭证 diff --git a/src-tauri/src/database/dao/agent.rs b/src-tauri/src/database/dao/agent.rs index 6470b53bf..e6c4bbcfe 100644 --- a/src-tauri/src/database/dao/agent.rs +++ b/src-tauri/src/database/dao/agent.rs @@ -183,7 +183,7 @@ impl AgentDao { let tool_calls_json = message .tool_calls .as_ref() - .map(|tc| serde_json::to_string(tc)) + .map(serde_json::to_string) .transpose() .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; diff --git a/src-tauri/src/database/dao/api_key_provider.rs b/src-tauri/src/database/dao/api_key_provider.rs index 3cefe562c..8bedf3da0 100644 --- a/src-tauri/src/database/dao/api_key_provider.rs +++ b/src-tauri/src/database/dao/api_key_provider.rs @@ -69,7 +69,7 @@ impl std::str::FromStr for ApiProviderType { "ollama" => Ok(ApiProviderType::Ollama), "new-api" => Ok(ApiProviderType::NewApi), "gateway" => Ok(ApiProviderType::Gateway), - _ => Err(format!("Invalid provider type: {}", s)), + _ => Err(format!("Invalid provider type: {s}")), } } } @@ -113,7 +113,7 @@ impl std::str::FromStr for ProviderGroup { "local" => Ok(ProviderGroup::Local), "specialized" => Ok(ProviderGroup::Specialized), "custom" => Ok(ProviderGroup::Custom), - _ => Err(format!("Invalid provider group: {}", s)), + _ => Err(format!("Invalid provider group: {s}")), } } } diff --git a/src-tauri/src/database/dao/chat.rs b/src-tauri/src/database/dao/chat.rs index 30f98ca70..165264c27 100644 --- a/src-tauri/src/database/dao/chat.rs +++ b/src-tauri/src/database/dao/chat.rs @@ -53,7 +53,7 @@ impl std::str::FromStr for ChatMode { "agent" => Ok(ChatMode::Agent), "general" => Ok(ChatMode::General), "creator" => Ok(ChatMode::Creator), - _ => Err(format!("未知的对话模式: {}", s)), + _ => Err(format!("未知的对话模式: {s}")), } } } @@ -279,7 +279,7 @@ impl ChatDao { let tool_calls_json = message .tool_calls .as_ref() - .map(|tc| serde_json::to_string(tc)) + .map(serde_json::to_string) .transpose() .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; @@ -387,9 +387,8 @@ impl ChatDao { serde_json::json!([{"type": "text", "text": content_json}]) }); - let tool_calls: Option = tool_calls_json - .map(|json| serde_json::from_str(&json).ok()) - .flatten(); + let tool_calls: Option = + tool_calls_json.and_then(|json| serde_json::from_str(&json).ok()); Ok(ChatMessage { id: row.get(0)?, diff --git a/src-tauri/src/database/dao/general_chat.rs b/src-tauri/src/database/dao/general_chat.rs index bdedc09c3..2d20e9dea 100644 --- a/src-tauri/src/database/dao/general_chat.rs +++ b/src-tauri/src/database/dao/general_chat.rs @@ -27,7 +27,7 @@ impl GeneralChatDao { let metadata_json = session .metadata .as_ref() - .map(|m| serde_json::to_string(m)) + .map(serde_json::to_string) .transpose() .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; @@ -167,14 +167,14 @@ impl GeneralChatDao { let blocks_json = message .blocks .as_ref() - .map(|b| serde_json::to_string(b)) + .map(serde_json::to_string) .transpose() .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; let metadata_json = message .metadata .as_ref() - .map(|m| serde_json::to_string(m)) + .map(serde_json::to_string) .transpose() .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; @@ -225,8 +225,7 @@ impl GeneralChatDao { FROM general_chat_messages WHERE session_id = ?1 AND id < ?2 ORDER BY created_at DESC - LIMIT {}", - lim + LIMIT {lim}" ) } (Some(lim), None) => { @@ -235,8 +234,7 @@ impl GeneralChatDao { FROM general_chat_messages WHERE session_id = ?1 ORDER BY created_at DESC - LIMIT {}", - lim + LIMIT {lim}" ) } (None, Some(_)) => { @@ -526,10 +524,10 @@ mod tests { for i in 1..=5 { let msg = create_test_message( - &format!("msg-{}", i), + &format!("msg-{i}"), "session-1", MessageRole::User, - &format!("消息 {}", i), + &format!("消息 {i}"), ); GeneralChatDao::add_message(&conn, &msg).unwrap(); } diff --git a/src-tauri/src/database/dao/installed_plugins.rs b/src-tauri/src/database/dao/installed_plugins.rs index d449ac0d8..657cce95b 100644 --- a/src-tauri/src/database/dao/installed_plugins.rs +++ b/src-tauri/src/database/dao/installed_plugins.rs @@ -65,7 +65,7 @@ impl PluginRow { fn into_record(self) -> Result { let source = deserialize_source(&self.source_type, self.source_data.as_deref())?; let installed_at = DateTime::parse_from_rfc3339(&self.installed_at) - .map_err(|e| format!("无效的时间格式: {}", e))? + .map_err(|e| format!("无效的时间格式: {e}"))? .with_timezone(&Utc); Ok(InstalledPluginRecord { @@ -112,14 +112,14 @@ fn deserialize_source( }), "github" => { let data: serde_json::Value = serde_json::from_str(source_data.unwrap_or("{}")) - .map_err(|e| format!("JSON 解析错误: {}", e))?; + .map_err(|e| format!("JSON 解析错误: {e}"))?; Ok(InstallSource::GitHub { owner: data["owner"].as_str().unwrap_or_default().to_string(), repo: data["repo"].as_str().unwrap_or_default().to_string(), tag: data["tag"].as_str().unwrap_or_default().to_string(), }) } - _ => Err(format!("未知的来源类型: {}", source_type)), + _ => Err(format!("未知的来源类型: {source_type}")), } } @@ -194,7 +194,7 @@ impl InstalledPluginsDao { }, ) .optional() - .map_err(|e| format!("数据库错误: {}", e))?; + .map_err(|e| format!("数据库错误: {e}"))?; match result { Some(row) => Ok(Some(row.into_record()?)), @@ -209,7 +209,7 @@ impl InstalledPluginsDao { "SELECT id, name, version, description, author, install_path, installed_at, source_type, source_data, enabled FROM installed_plugins ORDER BY installed_at DESC", ) - .map_err(|e| format!("数据库错误: {}", e))?; + .map_err(|e| format!("数据库错误: {e}"))?; let rows = stmt .query_map([], |row| { @@ -226,11 +226,11 @@ impl InstalledPluginsDao { enabled: row.get(9)?, }) }) - .map_err(|e| format!("数据库错误: {}", e))?; + .map_err(|e| format!("数据库错误: {e}"))?; let mut plugins = Vec::new(); for row in rows { - let row = row.map_err(|e| format!("数据库错误: {}", e))?; + let row = row.map_err(|e| format!("数据库错误: {e}"))?; plugins.push(row.into_record()?); } @@ -291,11 +291,11 @@ mod tests { fn create_test_plugin(id: &str) -> InstalledPluginRecord { InstalledPluginRecord { id: id.to_string(), - name: format!("Test Plugin {}", id), + name: format!("Test Plugin {id}"), version: "1.0.0".to_string(), description: Some("A test plugin".to_string()), author: Some("Test Author".to_string()), - install_path: PathBuf::from(format!("/plugins/{}", id)), + install_path: PathBuf::from(format!("/plugins/{id}")), installed_at: Utc::now(), source: InstallSource::Local { path: "/tmp/plugin.zip".to_string(), diff --git a/src-tauri/src/database/dao/orchestrator.rs b/src-tauri/src/database/dao/orchestrator.rs index 5ea097027..fac155349 100644 --- a/src-tauri/src/database/dao/orchestrator.rs +++ b/src-tauri/src/database/dao/orchestrator.rs @@ -517,7 +517,7 @@ impl OrchestratorDao { ) .map_err(|e| e.to_string())?; - let days_param = format!("-{} days", days); + let days_param = format!("-{days} days"); let rows = stmt .query_map(params![model_id, days_param], |row| { Ok(ModelUsageStats { @@ -540,7 +540,7 @@ impl OrchestratorDao { /// 清理旧的使用统计 pub fn cleanup_old_usage_stats(conn: &Connection, days: i32) -> Result { - let days_param = format!("-{} days", days); + let days_param = format!("-{days} days"); conn.execute( "DELETE FROM model_usage_stats WHERE date < date('now', ?1)", [days_param], diff --git a/src-tauri/src/database/migration.rs b/src-tauri/src/database/migration.rs index 7bc4f8978..b11b7c89b 100644 --- a/src-tauri/src/database/migration.rs +++ b/src-tauri/src/database/migration.rs @@ -26,7 +26,7 @@ pub fn migrate_from_json(conn: &Connection) -> Result<(), String> { let backup_path = config_path.with_file_name("config.json.backup"); if !backup_path.exists() { std::fs::copy(&config_path, &backup_path) - .map_err(|e| format!("备份旧配置失败: {}", e))?; + .map_err(|e| format!("备份旧配置失败: {e}"))?; } return Err( @@ -80,7 +80,7 @@ pub fn migrate_api_keys_to_pool(conn: &Connection) -> Result { JOIN api_key_providers p ON k.provider_id = p.id ORDER BY k.created_at ASC", ) - .map_err(|e| format!("准备查询语句失败: {}", e))?; + .map_err(|e| format!("准备查询语句失败: {e}"))?; let rows = stmt .query_map([], |row| { @@ -99,13 +99,13 @@ pub fn migrate_api_keys_to_pool(conn: &Connection) -> Result { provider_name: row.get(11)?, }) }) - .map_err(|e| format!("查询 API Keys 失败: {}", e))?; + .map_err(|e| format!("查询 API Keys 失败: {e}"))?; let mut migrated_count = 0; let now = chrono::Utc::now().timestamp(); for row_result in rows { - let row = row_result.map_err(|e| format!("读取行数据失败: {}", e))?; + let row = row_result.map_err(|e| format!("读取行数据失败: {e}"))?; // 检查是否已存在相同的凭证(通过 api_key_encrypted 判断) let exists: bool = conn @@ -228,7 +228,7 @@ pub fn migrate_api_keys_to_pool(conn: &Connection) -> Result { Option::::None, // proxy_url ], ) - .map_err(|e| format!("插入凭证失败: {}", e))?; + .map_err(|e| format!("插入凭证失败: {e}"))?; tracing::info!( "[迁移] 已迁移 API Key: {} -> {} (provider_type: {})", @@ -245,7 +245,7 @@ pub fn migrate_api_keys_to_pool(conn: &Connection) -> Result { "INSERT OR REPLACE INTO settings (key, value) VALUES ('migrated_api_keys_to_pool', 'true')", [], ) - .map_err(|e| format!("标记迁移完成失败: {}", e))?; + .map_err(|e| format!("标记迁移完成失败: {e}"))?; tracing::info!("[迁移] API Keys 迁移完成,共迁移 {} 条记录", migrated_count); @@ -349,7 +349,7 @@ pub fn migrate_provider_ids(conn: &Connection) -> Result { "UPDATE api_keys SET provider_id = ?1 WHERE provider_id = ?2", params![new_id, old_id], ) - .map_err(|e| format!("迁移 API Keys 失败: {}", e))?; + .map_err(|e| format!("迁移 API Keys 失败: {e}"))?; tracing::info!("[迁移] 已将 {} 的 API Keys 迁移到 {}", old_id, new_id); } else { @@ -358,13 +358,13 @@ pub fn migrate_provider_ids(conn: &Connection) -> Result { "UPDATE api_key_providers SET id = ?1 WHERE id = ?2", params![new_id, old_id], ) - .map_err(|e| format!("更新 Provider ID 失败: {}", e))?; + .map_err(|e| format!("更新 Provider ID 失败: {e}"))?; conn.execute( "UPDATE api_keys SET provider_id = ?1 WHERE provider_id = ?2", params![new_id, old_id], ) - .map_err(|e| format!("更新 API Keys provider_id 失败: {}", e))?; + .map_err(|e| format!("更新 API Keys provider_id 失败: {e}"))?; tracing::info!("[迁移] 已将 Provider {} 重命名为 {}", old_id, new_id); migrated_count += 1; @@ -377,7 +377,7 @@ pub fn migrate_provider_ids(conn: &Connection) -> Result { "DELETE FROM api_key_providers WHERE id = ?1", params![old_id], ) - .map_err(|e| format!("删除旧 Provider 失败: {}", e))?; + .map_err(|e| format!("删除旧 Provider 失败: {e}"))?; tracing::info!("[迁移] 已删除旧 Provider: {}", old_id); migrated_count += 1; @@ -388,7 +388,7 @@ pub fn migrate_provider_ids(conn: &Connection) -> Result { "INSERT OR REPLACE INTO settings (key, value) VALUES ('migrated_provider_ids_v1', 'true')", [], ) - .map_err(|e| format!("标记迁移完成失败: {}", e))?; + .map_err(|e| format!("标记迁移完成失败: {e}"))?; if migrated_count > 0 { tracing::info!( @@ -440,7 +440,7 @@ pub fn cleanup_legacy_api_key_credentials(conn: &Connection) -> Result Result Result(2)?, )) }) - .map_err(|e| format!("查询旧凭证失败: {}", e))?; + .map_err(|e| format!("查询旧凭证失败: {e}"))?; for row_result in rows { if let Ok((uuid, name, provider_type)) = row_result { @@ -483,14 +483,14 @@ pub fn cleanup_legacy_api_key_credentials(conn: &Connection) -> Result Result Result Result { "SELECT id, name, created_at, updated_at, metadata FROM general_chat_sessions", ) - .map_err(|e| format!("准备查询语句失败: {}", e))?; + .map_err(|e| format!("准备查询语句失败: {e}"))?; let sessions: Vec<(String, String, i64, i64, Option)> = stmt .query_map([], |row| { @@ -641,7 +641,7 @@ fn migrate_general_sessions(conn: &Connection) -> Result { row.get(4)?, )) }) - .map_err(|e| format!("查询会话失败: {}", e))? + .map_err(|e| format!("查询会话失败: {e}"))? .filter_map(|r| r.ok()) .collect(); @@ -678,7 +678,7 @@ fn migrate_general_sessions(conn: &Connection) -> Result { updated_str, ], ) - .map_err(|e| format!("插入会话失败: {}", e))?; + .map_err(|e| format!("插入会话失败: {e}"))?; count += 1; } @@ -693,7 +693,7 @@ fn migrate_general_messages(conn: &Connection) -> Result { "SELECT id, session_id, role, content, blocks, status, created_at, metadata FROM general_chat_messages", ) - .map_err(|e| format!("准备查询语句失败: {}", e))?; + .map_err(|e| format!("准备查询语句失败: {e}"))?; #[allow(clippy::type_complexity)] let messages: Vec<( @@ -718,7 +718,7 @@ fn migrate_general_messages(conn: &Connection) -> Result { row.get(7)?, )) }) - .map_err(|e| format!("查询消息失败: {}", e))? + .map_err(|e| format!("查询消息失败: {e}"))? .filter_map(|r| r.ok()) .collect(); @@ -755,7 +755,7 @@ fn migrate_general_messages(conn: &Connection) -> Result { Option::::None, ], ) - .map_err(|e| format!("插入消息失败: {}", e))?; + .map_err(|e| format!("插入消息失败: {e}"))?; count += 1; } diff --git a/src-tauri/src/database/mod.rs b/src-tauri/src/database/mod.rs index 0bbdc4779..6e0555740 100644 --- a/src-tauri/src/database/mod.rs +++ b/src-tauri/src/database/mod.rs @@ -9,12 +9,23 @@ use std::sync::{Arc, Mutex}; pub type DbConnection = Arc>; +/// 获取数据库连接锁(自动处理 poisoned lock) +pub fn lock_db(db: &DbConnection) -> Result, String> { + match db.lock() { + Ok(guard) => Ok(guard), + Err(poisoned) => { + tracing::warn!("[数据库] 检测到数据库锁被污染,尝试恢复: {}", poisoned); + db.clear_poison(); + Ok(poisoned.into_inner()) + } + } +} + /// 获取数据库文件路径 pub fn get_db_path() -> Result { let home = dirs::home_dir().ok_or_else(|| "无法获取主目录".to_string())?; let db_dir = home.join(".proxycast"); - std::fs::create_dir_all(&db_dir) - .map_err(|e| format!("无法创建数据库目录 {:?}: {}", db_dir, e))?; + std::fs::create_dir_all(&db_dir).map_err(|e| format!("无法创建数据库目录 {db_dir:?}: {e}"))?; Ok(db_dir.join("proxycast.db")) } @@ -25,7 +36,7 @@ pub fn init_database() -> Result { // 设置 busy_timeout 为 5 秒,避免 "database is locked" 错误 conn.busy_timeout(std::time::Duration::from_secs(5)) - .map_err(|e| format!("设置 busy_timeout 失败: {}", e))?; + .map_err(|e| format!("设置 busy_timeout 失败: {e}"))?; // 创建表结构 schema::create_tables(&conn).map_err(|e| e.to_string())?; diff --git a/src-tauri/src/dev_bridge.rs b/src-tauri/src/dev_bridge.rs index 8990fd484..648e540ee 100644 --- a/src-tauri/src/dev_bridge.rs +++ b/src-tauri/src/dev_bridge.rs @@ -94,12 +94,12 @@ impl DevBridgeServer { let listener = match tokio::net::TcpListener::bind(&addr).await { Ok(l) => l, Err(e) => { - eprintln!("[DevBridge] 绑定失败: {} (地址: {})", e, addr); + eprintln!("[DevBridge] 绑定失败: {e} (地址: {addr})"); return Err(e.into()); } }; - eprintln!("[DevBridge] 正在监听: http://{}", addr); + eprintln!("[DevBridge] 正在监听: http://{addr}"); // 直接运行服务器(不使用 graceful_shutdown) // 服务器将持续运行直到应用退出 diff --git a/src-tauri/src/dev_bridge/dispatcher.rs b/src-tauri/src/dev_bridge/dispatcher.rs index ca63f604d..40b7ea8f6 100644 --- a/src-tauri/src/dev_bridge/dispatcher.rs +++ b/src-tauri/src/dev_bridge/dispatcher.rs @@ -214,9 +214,9 @@ pub async fn handle_command( updated_at: now, }; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; AgentDao::create_session(&conn, &session) - .map_err(|e| format!("创建会话失败: {}", e))?; + .map_err(|e| format!("创建会话失败: {e}"))?; Ok(serde_json::json!({ "session_id": session_id, @@ -234,9 +234,9 @@ pub async fn handle_command( if let Some(db) = &state.db { use crate::database::dao::agent::AgentDao; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let sessions = AgentDao::list_sessions(&conn) - .map_err(|e| format!("获取会话列表失败: {}", e))?; + .map_err(|e| format!("获取会话列表失败: {e}"))?; let result: Vec = sessions .into_iter() @@ -269,10 +269,10 @@ pub async fn handle_command( if let Some(db) = &state.db { use crate::database::dao::agent::AgentDao; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let session = AgentDao::get_session(&conn, &session_id) - .map_err(|e| format!("获取会话失败: {}", e))? - .ok_or_else(|| "会话不存在")?; + .map_err(|e| format!("获取会话失败: {e}"))? + .ok_or("会话不存在")?; let messages_count = AgentDao::get_message_count(&conn, &session_id).unwrap_or(0); @@ -299,9 +299,9 @@ pub async fn handle_command( if let Some(db) = &state.db { use crate::database::dao::agent::AgentDao; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; AgentDao::delete_session(&conn, &session_id) - .map_err(|e| format!("删除会话失败: {}", e))?; + .map_err(|e| format!("删除会话失败: {e}"))?; Ok(serde_json::json!({ "success": true })) } else { @@ -319,9 +319,9 @@ pub async fn handle_command( if let Some(db) = &state.db { use crate::database::dao::agent::AgentDao; - let conn = db.lock().map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let messages = AgentDao::get_messages(&conn, &session_id) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; Ok(serde_json::to_value(messages)?) } else { @@ -330,8 +330,7 @@ pub async fn handle_command( } _ => Err(format!( - "[DevBridge] 未知命令: '{}'. 如需此命令,请将其添加到 dispatcher.rs 的 handle_command 函数中。", - cmd + "[DevBridge] 未知命令: '{cmd}'. 如需此命令,请将其添加到 dispatcher.rs 的 handle_command 函数中。" ) .into()), } diff --git a/src-tauri/src/flow_monitor/batch_ops.rs b/src-tauri/src/flow_monitor/batch_ops.rs index 3a9b402f9..f2e221fc9 100644 --- a/src-tauri/src/flow_monitor/batch_ops.rs +++ b/src-tauri/src/flow_monitor/batch_ops.rs @@ -318,13 +318,13 @@ impl BatchOperations { Ok(Some(_)) => {} Ok(None) => { for flow_id in flow_ids { - result.record_failure(flow_id, format!("会话不存在: {}", session_id)); + result.record_failure(flow_id, format!("会话不存在: {session_id}")); } return; } Err(e) => { for flow_id in flow_ids { - result.record_failure(flow_id, format!("查询会话失败: {}", e)); + result.record_failure(flow_id, format!("查询会话失败: {e}")); } return; } @@ -344,7 +344,7 @@ impl BatchOperations { result.record_success(); } Err(e) => { - result.record_failure(flow_id, format!("添加到会话失败: {}", e)); + result.record_failure(flow_id, format!("添加到会话失败: {e}")); } } } @@ -404,7 +404,7 @@ mod property_tests { // 创建测试 Flow let mut flow_ids = Vec::new(); for i in 0..flow_count { - let id = create_test_flow(&monitor, &format!("flow-{}", i)).await; + let id = create_test_flow(&monitor, &format!("flow-{i}")).await; flow_ids.push(id); } @@ -446,14 +446,14 @@ mod property_tests { // 创建有效的 Flow let mut valid_flow_ids = Vec::new(); for i in 0..valid_flow_count { - let id = create_test_flow(&monitor, &format!("valid-flow-{}", i)).await; + let id = create_test_flow(&monitor, &format!("valid-flow-{i}")).await; valid_flow_ids.push(id); } // 创建无效的 Flow ID(不存在的) let mut invalid_flow_ids = Vec::new(); for i in 0..invalid_flow_count { - invalid_flow_ids.push(format!("invalid-flow-{}", i)); + invalid_flow_ids.push(format!("invalid-flow-{i}")); } // 混合有效和无效的 Flow ID @@ -512,18 +512,18 @@ mod property_tests { // 创建有效的 Flow let mut valid_flow_ids = Vec::new(); for i in 0..valid_flow_count { - let id = create_test_flow(&monitor, &format!("valid-flow-{}", i)).await; + let id = create_test_flow(&monitor, &format!("valid-flow-{i}")).await; valid_flow_ids.push(id); } // 创建无效的 Flow ID let mut invalid_flow_ids = Vec::new(); for i in 0..invalid_flow_count { - invalid_flow_ids.push(format!("invalid-flow-{}", i)); + invalid_flow_ids.push(format!("invalid-flow-{i}")); } // 创建标签列表 - let tags: Vec = (0..tag_count).map(|i| format!("tag-{}", i)).collect(); + let tags: Vec = (0..tag_count).map(|i| format!("tag-{i}")).collect(); // 混合有效和无效的 Flow ID let mut all_flow_ids = valid_flow_ids.clone(); diff --git a/src-tauri/src/flow_monitor/bookmark.rs b/src-tauri/src/flow_monitor/bookmark.rs index ec6e6bf7d..801da8daf 100644 --- a/src-tauri/src/flow_monitor/bookmark.rs +++ b/src-tauri/src/flow_monitor/bookmark.rs @@ -901,7 +901,7 @@ mod property_tests { let mut added_bookmarks = Vec::new(); for (i, (flow_id, name, group)) in bookmarks.iter().enumerate() { // 确保 flow_id 唯一 - let unique_flow_id = format!("{}_{}", flow_id, i); + let unique_flow_id = format!("{flow_id}_{i}"); let bookmark = manager1.add(&unique_flow_id, name.as_deref(), group.as_deref()).unwrap(); added_bookmarks.push(bookmark); } @@ -984,7 +984,7 @@ mod property_tests { // 添加所有书签 let mut added_ids = Vec::new(); for (i, (flow_id, name, group)) in bookmarks.iter().enumerate() { - let unique_flow_id = format!("{}_{}", flow_id, i); + let unique_flow_id = format!("{flow_id}_{i}"); let bookmark = manager.add(&unique_flow_id, name.as_deref(), group.as_deref()).unwrap(); added_ids.push(bookmark.id); } diff --git a/src-tauri/src/flow_monitor/code_exporter.rs b/src-tauri/src/flow_monitor/code_exporter.rs index 0dbd31001..16ea433a9 100644 --- a/src-tauri/src/flow_monitor/code_exporter.rs +++ b/src-tauri/src/flow_monitor/code_exporter.rs @@ -15,8 +15,10 @@ use super::models::{LLMFlow, LLMRequest}; /// 代码导出格式 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum CodeFormat { /// curl 命令 + #[default] Curl, /// Python 代码 Python, @@ -26,12 +28,6 @@ pub enum CodeFormat { JavaScript, } -impl Default for CodeFormat { - fn default() -> Self { - CodeFormat::Curl - } -} - // ============================================================================ // 代码导出器 // ============================================================================ @@ -90,7 +86,7 @@ impl CodeExporter { } else { format!("http://localhost{}", request.path) }; - parts.push(format!("'{}'", url)); + parts.push(format!("'{url}'")); // 添加请求头 for (key, value) in &request.headers { @@ -102,7 +98,7 @@ impl CodeExporter { } else { escape_shell_string(value) }; - parts.push(format!("-H '{}: {}'", key, header_value)); + parts.push(format!("-H '{key}: {header_value}'")); } // 确保有 Content-Type 头 @@ -153,7 +149,7 @@ impl CodeExporter { } else { format!("http://localhost{}", request.path) }; - code.push_str(&format!("url = \"{}\"\n\n", url)); + code.push_str(&format!("url = \"{url}\"\n\n")); // 请求头 code.push_str("headers = {\n"); @@ -171,9 +167,9 @@ impl CodeExporter { }; if key.to_lowercase() == "authorization" || key.to_lowercase() == "x-api-key" { - code.push_str(&format!(" \"{}\": {},\n", key, header_value)); + code.push_str(&format!(" \"{key}\": {header_value},\n")); } else { - code.push_str(&format!(" \"{}\": {},\n", key, header_value)); + code.push_str(&format!(" \"{key}\": {header_value},\n")); } } if !has_content_type { @@ -184,7 +180,7 @@ impl CodeExporter { // 请求体 if !request.body.is_null() { let body_str = serde_json::to_string_pretty(&request.body).unwrap_or_default(); - code.push_str(&format!("data = {}\n\n", body_str)); + code.push_str(&format!("data = {body_str}\n\n")); } else { code.push_str("data = {}\n\n"); } @@ -250,7 +246,7 @@ impl CodeExporter { } else { format!("'{}'", escape_js_string(value)) }; - code.push_str(&format!(" '{}': {},\n", key, header_value)); + code.push_str(&format!(" '{key}': {header_value},\n")); } if !has_content_type { code.push_str(" 'Content-Type': 'application/json',\n"); @@ -330,7 +326,7 @@ impl CodeExporter { } else { format!("'{}'", escape_js_string(value)) }; - code.push_str(&format!(" '{}': {},\n", key, header_value)); + code.push_str(&format!(" '{key}': {header_value},\n")); } if !has_content_type { code.push_str(" 'Content-Type': 'application/json',\n"); diff --git a/src-tauri/src/flow_monitor/diff.rs b/src-tauri/src/flow_monitor/diff.rs index 962b4ec7b..649ea61de 100644 --- a/src-tauri/src/flow_monitor/diff.rs +++ b/src-tauri/src/flow_monitor/diff.rs @@ -20,7 +20,7 @@ use super::models::{LLMFlow, Message, MessageContent, TokenUsage}; // ============================================================================ /// 差异类型 -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum DiffType { /// 新增 Added, @@ -29,15 +29,10 @@ pub enum DiffType { /// 修改 Modified, /// 未变化 + #[default] Unchanged, } -impl Default for DiffType { - fn default() -> Self { - DiffType::Unchanged - } -} - // ============================================================================ // 差异项 // ============================================================================ @@ -344,36 +339,30 @@ impl FlowDiff { let mut diffs = Vec::new(); // 对比模型 - if !config.should_ignore("request.model") { - if left.model != right.model { - diffs.push(DiffItem::modified( - "request.model", - Value::String(left.model.clone()), - Value::String(right.model.clone()), - )); - } + if !config.should_ignore("request.model") && left.model != right.model { + diffs.push(DiffItem::modified( + "request.model", + Value::String(left.model.clone()), + Value::String(right.model.clone()), + )); } // 对比方法 - if !config.should_ignore("request.method") { - if left.method != right.method { - diffs.push(DiffItem::modified( - "request.method", - Value::String(left.method.clone()), - Value::String(right.method.clone()), - )); - } + if !config.should_ignore("request.method") && left.method != right.method { + diffs.push(DiffItem::modified( + "request.method", + Value::String(left.method.clone()), + Value::String(right.method.clone()), + )); } // 对比路径 - if !config.should_ignore("request.path") { - if left.path != right.path { - diffs.push(DiffItem::modified( - "request.path", - Value::String(left.path.clone()), - Value::String(right.path.clone()), - )); - } + if !config.should_ignore("request.path") && left.path != right.path { + diffs.push(DiffItem::modified( + "request.path", + Value::String(left.path.clone()), + Value::String(right.path.clone()), + )); } // 对比系统提示词 @@ -638,8 +627,8 @@ impl FlowDiff { if !config.should_ignore("metadata.provider") && left.provider != right.provider { diffs.push(DiffItem::modified( "metadata.provider", - serde_json::to_value(&left.provider).unwrap_or(Value::Null), - serde_json::to_value(&right.provider).unwrap_or(Value::Null), + serde_json::to_value(left.provider).unwrap_or(Value::Null), + serde_json::to_value(right.provider).unwrap_or(Value::Null), )); } @@ -731,12 +720,12 @@ impl FlowDiff { /// 对比单个消息的内容 fn diff_message_content(left: &Message, right: &Message, index: usize) -> Vec { let mut diffs = Vec::new(); - let prefix = format!("messages[{}]", index); + let prefix = format!("messages[{index}]"); // 对比角色 if left.role != right.role { diffs.push(DiffItem::modified( - format!("{}.role", prefix), + format!("{prefix}.role"), serde_json::to_value(&left.role).unwrap_or(Value::Null), serde_json::to_value(&right.role).unwrap_or(Value::Null), )); @@ -747,7 +736,7 @@ impl FlowDiff { let right_text = Self::get_message_text(&right.content); if left_text != right_text { diffs.push(DiffItem::modified( - format!("{}.content", prefix), + format!("{prefix}.content"), Value::String(left_text), Value::String(right_text), )); @@ -757,20 +746,20 @@ impl FlowDiff { match (&left.name, &right.name) { (Some(l), Some(r)) if l != r => { diffs.push(DiffItem::modified( - format!("{}.name", prefix), + format!("{prefix}.name"), Value::String(l.clone()), Value::String(r.clone()), )); } (Some(l), None) => { diffs.push(DiffItem::removed( - format!("{}.name", prefix), + format!("{prefix}.name"), Value::String(l.clone()), )); } (None, Some(r)) => { diffs.push(DiffItem::added( - format!("{}.name", prefix), + format!("{prefix}.name"), Value::String(r.clone()), )); } @@ -781,20 +770,20 @@ impl FlowDiff { match (&left.tool_calls, &right.tool_calls) { (Some(l), Some(r)) if l.len() != r.len() => { diffs.push(DiffItem::modified( - format!("{}.tool_calls.count", prefix), + format!("{prefix}.tool_calls.count"), serde_json::json!(l.len()), serde_json::json!(r.len()), )); } (Some(l), None) => { diffs.push(DiffItem::removed( - format!("{}.tool_calls", prefix), + format!("{prefix}.tool_calls"), serde_json::to_value(l).unwrap_or(Value::Null), )); } (None, Some(r)) => { diffs.push(DiffItem::added( - format!("{}.tool_calls", prefix), + format!("{prefix}.tool_calls"), serde_json::to_value(r).unwrap_or(Value::Null), )); } @@ -864,7 +853,7 @@ impl FlowDiff { let new_path = if path.is_empty() { key.clone() } else { - format!("{}.{}", path, key) + format!("{path}.{key}") }; match (l.get(key), r.get(key)) { @@ -888,7 +877,7 @@ impl FlowDiff { (Value::Array(l), Value::Array(r)) => { let max_len = l.len().max(r.len()); for i in 0..max_len { - let new_path = format!("{}[{}]", path, i); + let new_path = format!("{path}[{i}]"); match (l.get(i), r.get(i)) { (Some(lv), Some(rv)) => { diffs.extend(Self::diff_json(lv, rv, &new_path, config)); diff --git a/src-tauri/src/flow_monitor/enhanced_stats.rs b/src-tauri/src/flow_monitor/enhanced_stats.rs index e47bf188b..8d12b65ae 100644 --- a/src-tauri/src/flow_monitor/enhanced_stats.rs +++ b/src-tauri/src/flow_monitor/enhanced_stats.rs @@ -27,7 +27,7 @@ pub struct TimeSeriesPoint { } /// 分布数据 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Distribution { /// 分布桶 (标签, 数量) pub buckets: Vec<(String, u64)>, @@ -35,15 +35,6 @@ pub struct Distribution { pub total: u64, } -impl Default for Distribution { - fn default() -> Self { - Self { - buckets: Vec::new(), - total: 0, - } - } -} - /// 趋势数据 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TrendData { @@ -117,8 +108,10 @@ impl Default for StatsTimeRange { /// 统计报告格式 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum ReportFormat { /// JSON 格式 + #[default] Json, /// Markdown 格式 Markdown, @@ -126,12 +119,6 @@ pub enum ReportFormat { Csv, } -impl Default for ReportFormat { - fn default() -> Self { - ReportFormat::Json - } -} - // ============================================================================ // 增强统计服务 // ============================================================================ @@ -506,7 +493,7 @@ impl EnhancedStatsService { md.push_str("| 模型 | Token 数 |\n"); md.push_str("|------|----------|\n"); for (model, tokens) in &stats.token_by_model.buckets { - md.push_str(&format!("| {} | {} |\n", model, tokens)); + md.push_str(&format!("| {model} | {tokens} |\n")); } md.push_str(&format!( "| **总计** | **{}** |\n\n", @@ -527,7 +514,7 @@ impl EnhancedStatsService { md.push_str("| 延迟范围 | 请求数 |\n"); md.push_str("|----------|--------|\n"); for (range, count) in &stats.latency_histogram.buckets { - md.push_str(&format!("| {} | {} |\n", range, count)); + md.push_str(&format!("| {range} | {count} |\n")); } md.push('\n'); @@ -537,7 +524,7 @@ impl EnhancedStatsService { md.push_str("| 错误类型 | 数量 |\n"); md.push_str("|----------|------|\n"); for (error_type, count) in &stats.error_distribution.buckets { - md.push_str(&format!("| {} | {} |\n", error_type, count)); + md.push_str(&format!("| {error_type} | {count} |\n")); } md.push('\n'); } @@ -553,7 +540,7 @@ impl EnhancedStatsService { csv.push_str("# Token Distribution by Model\n"); csv.push_str("Model,Tokens\n"); for (model, tokens) in &stats.token_by_model.buckets { - csv.push_str(&format!("{},{}\n", model, tokens)); + csv.push_str(&format!("{model},{tokens}\n")); } csv.push('\n'); @@ -561,7 +548,7 @@ impl EnhancedStatsService { csv.push_str("# Success Rate by Provider\n"); csv.push_str("Provider,SuccessRate\n"); for (provider, rate) in &stats.success_by_provider { - csv.push_str(&format!("{},{:.4}\n", provider, rate)); + csv.push_str(&format!("{provider},{rate:.4}\n")); } csv.push('\n'); @@ -569,7 +556,7 @@ impl EnhancedStatsService { csv.push_str("# Latency Histogram\n"); csv.push_str("Range,Count\n"); for (range, count) in &stats.latency_histogram.buckets { - csv.push_str(&format!("{},{}\n", range, count)); + csv.push_str(&format!("{range},{count}\n")); } csv.push('\n'); @@ -577,7 +564,7 @@ impl EnhancedStatsService { csv.push_str("# Error Distribution\n"); csv.push_str("ErrorType,Count\n"); for (error_type, count) in &stats.error_distribution.buckets { - csv.push_str(&format!("{},{}\n", error_type, count)); + csv.push_str(&format!("{error_type},{count}\n")); } csv diff --git a/src-tauri/src/flow_monitor/exporter.rs b/src-tauri/src/flow_monitor/exporter.rs index ac4e1c8ce..59671f06b 100644 --- a/src-tauri/src/flow_monitor/exporter.rs +++ b/src-tauri/src/flow_monitor/exporter.rs @@ -21,10 +21,12 @@ use crate::ProviderType; /// 导出格式 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum ExportFormat { /// HAR (HTTP Archive) 格式 HAR, /// JSON 格式 + #[default] JSON, /// JSONL (JSON Lines) 格式 JSONL, @@ -34,12 +36,6 @@ pub enum ExportFormat { CSV, } -impl Default for ExportFormat { - fn default() -> Self { - ExportFormat::JSON - } -} - // ============================================================================ // 导出选项 // ============================================================================ @@ -794,7 +790,7 @@ impl FlowExporter { }), streaming: request.parameters.stream, ttfb_ms: flow.timestamps.ttfb_ms, - stop_reason: response.and_then(|r| r.stop_reason.as_ref().map(|s| format!("{:?}", s))), + stop_reason: response.and_then(|r| r.stop_reason.as_ref().map(|s| format!("{s:?}"))), has_tool_calls: response.map(|r| !r.tool_calls.is_empty()).unwrap_or(false), has_thinking: response.map(|r| r.thinking.is_some()).unwrap_or(false), annotations: if flow.annotations.starred @@ -880,7 +876,7 @@ impl FlowExporter { .map(|(i, f)| { let md = self.flow_to_markdown(f); if i > 0 { - format!("\n---\n\n{}", md) + format!("\n---\n\n{md}") } else { md } @@ -909,7 +905,7 @@ impl FlowExporter { )); md.push_str(&format!("- **耗时**: {} ms\n", flow.timestamps.duration_ms)); if let Some(ttfb) = flow.timestamps.ttfb_ms { - md.push_str(&format!("- **TTFB**: {} ms\n", ttfb)); + md.push_str(&format!("- **TTFB**: {ttfb} ms\n")); } md.push_str(&format!("- **流式**: {}\n", flow.request.parameters.stream)); md.push('\n'); @@ -930,10 +926,10 @@ impl FlowExporter { response.usage.total_tokens )); if let Some(cache_read) = response.usage.cache_read_tokens { - md.push_str(&format!("- **缓存读取**: {}\n", cache_read)); + md.push_str(&format!("- **缓存读取**: {cache_read}\n")); } if let Some(thinking) = response.usage.thinking_tokens { - md.push_str(&format!("- **思维链 Token**: {}\n", thinking)); + md.push_str(&format!("- **思维链 Token**: {thinking}\n")); } md.push('\n'); } @@ -1023,7 +1019,7 @@ impl FlowExporter { // 停止原因 if let Some(ref stop_reason) = response.stop_reason { - md.push_str(&format!("**停止原因**: {:?}\n\n", stop_reason)); + md.push_str(&format!("**停止原因**: {stop_reason:?}\n\n")); } } @@ -1033,7 +1029,7 @@ impl FlowExporter { md.push_str(&format!("- **类型**: {:?}\n", error.error_type)); md.push_str(&format!("- **消息**: {}\n", error.message)); if let Some(code) = error.status_code { - md.push_str(&format!("- **状态码**: {}\n", code)); + md.push_str(&format!("- **状态码**: {code}\n")); } md.push_str(&format!("- **可重试**: {}\n", error.retryable)); md.push('\n'); @@ -1049,7 +1045,7 @@ impl FlowExporter { md.push_str("- ⭐ **已收藏**\n"); } if let Some(ref marker) = flow.annotations.marker { - md.push_str(&format!("- **标记**: {}\n", marker)); + md.push_str(&format!("- **标记**: {marker}\n")); } if !flow.annotations.tags.is_empty() { md.push_str(&format!( @@ -1058,7 +1054,7 @@ impl FlowExporter { )); } if let Some(ref comment) = flow.annotations.comment { - md.push_str(&format!("- **评论**: {}\n", comment)); + md.push_str(&format!("- **评论**: {comment}\n")); } md.push('\n'); } @@ -1721,7 +1717,7 @@ mod property_tests { // 验证每行都能反序列化 for (i, line) in lines.iter().enumerate() { let deserialized: LLMFlow = serde_json::from_str(line) - .expect(&format!("第 {} 行应该能够反序列化", i)); + .unwrap_or_else(|_| panic!("第 {i} 行应该能够反序列化")); prop_assert_eq!( &flows[i].id, &deserialized.id, @@ -1843,7 +1839,7 @@ mod redaction_property_tests { "[a-z]{3,10}", prop_oneof!["com", "org", "net", "io"], ) - .prop_map(|(user, domain, tld)| format!("{}@{}.{}", user, domain, tld)) + .prop_map(|(user, domain, tld)| format!("{user}@{domain}.{tld}")) } /// 生成随机中国手机号 @@ -1852,17 +1848,17 @@ mod redaction_property_tests { prop_oneof![Just("13"), Just("15"), Just("18"), Just("19")], "[0-9]{9}", ) - .prop_map(|(prefix, suffix)| format!("{}{}", prefix, suffix)) + .prop_map(|(prefix, suffix)| format!("{prefix}{suffix}")) } /// 生成随机 API 密钥 fn arb_api_key() -> impl Strategy { - "[a-zA-Z0-9]{20,40}".prop_map(|s| format!("sk-{}", s)) + "[a-zA-Z0-9]{20,40}".prop_map(|s| format!("sk-{s}")) } /// 生成随机 Bearer Token fn arb_bearer_token() -> impl Strategy { - "[a-zA-Z0-9_.-]{20,50}".prop_map(|s| format!("Bearer {}", s)) + "[a-zA-Z0-9_.-]{20,50}".prop_map(|s| format!("Bearer {s}")) } /// 生成包含敏感数据的文本 @@ -1870,27 +1866,27 @@ mod redaction_property_tests { prop_oneof![ // 包含邮箱 arb_email().prop_map(|email| { - let text = format!("Contact me at {} for more info.", email); + let text = format!("Contact me at {email} for more info."); (text, vec![email]) }), // 包含手机号 arb_phone_cn().prop_map(|phone| { - let text = format!("My phone number is {}.", phone); + let text = format!("My phone number is {phone}."); (text, vec![phone]) }), // 包含 API 密钥 arb_api_key().prop_map(|key| { - let text = format!("Use this API key: {}", key); + let text = format!("Use this API key: {key}"); (text, vec![key]) }), // 包含 Bearer Token arb_bearer_token().prop_map(|token| { - let text = format!("Authorization: {}", token); + let text = format!("Authorization: {token}"); (text, vec![token]) }), // 包含多种敏感数据 (arb_email(), arb_phone_cn()).prop_map(|(email, phone)| { - let text = format!("Email: {}, Phone: {}", email, phone); + let text = format!("Email: {email}, Phone: {phone}"); (text, vec![email, phone]) }), ] @@ -2107,7 +2103,7 @@ mod redaction_property_tests { #[test] fn prop_redact_email(email in arb_email()) { let redactor = Redactor::with_defaults(); - let text = format!("Contact: {}", email); + let text = format!("Contact: {email}"); let redacted = redactor.redact(&text); @@ -2129,7 +2125,7 @@ mod redaction_property_tests { #[test] fn prop_redact_phone(phone in arb_phone_cn()) { let redactor = Redactor::with_defaults(); - let text = format!("Phone: {}", phone); + let text = format!("Phone: {phone}"); let redacted = redactor.redact(&text); @@ -2151,7 +2147,7 @@ mod redaction_property_tests { #[test] fn prop_redact_api_key(key in arb_api_key()) { let redactor = Redactor::with_defaults(); - let text = format!("API Key: {}", key); + let text = format!("API Key: {key}"); let redacted = redactor.redact(&text); diff --git a/src-tauri/src/flow_monitor/file_store.rs b/src-tauri/src/flow_monitor/file_store.rs index 5fe7a3e5c..f3895d431 100644 --- a/src-tauri/src/flow_monitor/file_store.rs +++ b/src-tauri/src/flow_monitor/file_store.rs @@ -157,11 +157,8 @@ impl FlowIndexRecord { has_tool_calls: flow .response .as_ref() - .map_or(false, |r| !r.tool_calls.is_empty()), - has_thinking: flow - .response - .as_ref() - .map_or(false, |r| r.thinking.is_some()), + .is_some_and(|r| !r.tool_calls.is_empty()), + has_thinking: flow.response.as_ref().is_some_and(|r| r.thinking.is_some()), file_path: file_path.to_string(), file_offset, content_preview, @@ -202,7 +199,7 @@ impl FlowWriter { fn write(&mut self, flow: &LLMFlow) -> Result { let offset = self.current_offset; let json = serde_json::to_string(flow)?; - let line = format!("{}\n", json); + let line = format!("{json}\n"); let bytes = line.as_bytes(); self.file.write_all(bytes)?; @@ -391,7 +388,7 @@ impl FlowFileStore { fs::create_dir_all(&date_dir)?; // 创建文件路径 - let file_name = format!("flows_{:03}.jsonl", index); + let file_name = format!("flows_{index:03}.jsonl"); let file_path = date_dir.join(file_name); FlowWriter::new(file_path) @@ -632,7 +629,7 @@ impl FlowFileStore { let placeholders: Vec = providers.iter().map(|_| "?".to_string()).collect(); conditions.push(format!("provider IN ({})", placeholders.join(", "))); for p in providers { - params_vec.push(Box::new(format!("{:?}", p))); + params_vec.push(Box::new(format!("{p:?}"))); } } } @@ -643,7 +640,7 @@ impl FlowFileStore { let placeholders: Vec = states.iter().map(|_| "?".to_string()).collect(); conditions.push(format!("status IN ({})", placeholders.join(", "))); for s in states { - params_vec.push(Box::new(format!("{:?}", s))); + params_vec.push(Box::new(format!("{s:?}"))); } } } @@ -674,8 +671,7 @@ impl FlowFileStore { }; let sql = format!( - "SELECT file_path, file_offset FROM flow_index {} ORDER BY created_at DESC LIMIT ? OFFSET ?", - where_clause + "SELECT file_path, file_offset FROM flow_index {where_clause} ORDER BY created_at DESC LIMIT ? OFFSET ?" ); params_vec.push(Box::new(limit as i64)); @@ -879,7 +875,7 @@ impl FlowFileStore { if let Ok(mut dir_entries) = fs::read_dir(&path) { let has_jsonl = dir_entries.any(|e| { e.ok() - .map(|e| e.path().extension().map_or(false, |ext| ext == "jsonl")) + .map(|e| e.path().extension().is_some_and(|ext| ext == "jsonl")) .unwrap_or(false) }); @@ -976,7 +972,7 @@ mod tests { // 写入多个 Flow for i in 0..10 { - let flow = create_test_flow(&format!("flow-{}", i), "gpt-4", ProviderType::OpenAI); + let flow = create_test_flow(&format!("flow-{i}"), "gpt-4", ProviderType::OpenAI); store.write(&flow).unwrap(); } @@ -985,7 +981,7 @@ mod tests { // 验证可以读取每个 for i in 0..10 { - let retrieved = store.get(&format!("flow-{}", i)).unwrap(); + let retrieved = store.get(&format!("flow-{i}")).unwrap(); assert!(retrieved.is_some()); } } @@ -1040,13 +1036,13 @@ mod tests { // 写入多个 Flow,应该触发轮转 for i in 0..5 { - let flow = create_test_flow(&format!("flow-{}", i), "gpt-4", ProviderType::OpenAI); + let flow = create_test_flow(&format!("flow-{i}"), "gpt-4", ProviderType::OpenAI); store.write(&flow).unwrap(); } // 验证所有 Flow 都可以读取 for i in 0..5 { - let retrieved = store.get(&format!("flow-{}", i)).unwrap(); + let retrieved = store.get(&format!("flow-{i}")).unwrap(); assert!(retrieved.is_some()); } } @@ -1059,7 +1055,7 @@ mod tests { // 写入一些 Flow for i in 0..5 { - let flow = create_test_flow(&format!("flow-{}", i), "gpt-4", ProviderType::OpenAI); + let flow = create_test_flow(&format!("flow-{i}"), "gpt-4", ProviderType::OpenAI); store.write(&flow).unwrap(); } @@ -1280,7 +1276,7 @@ mod property_tests { let original_id = flow.id.clone(); let original_model = flow.request.model.clone(); - let original_provider = flow.metadata.provider.clone(); + let original_provider = flow.metadata.provider; let original_state = flow.state.clone(); let original_content = flow.response.as_ref().map(|r| r.content.clone()); let original_starred = flow.annotations.starred; @@ -1328,7 +1324,7 @@ mod property_tests { // 创建并写入多个 Flow let mut original_flows = Vec::new(); for i in 0..flow_count { - let id = format!("flow-{:04}", i); + let id = format!("flow-{i:04}"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), @@ -1385,7 +1381,7 @@ mod property_tests { // 创建并写入多个 Flow let mut original_ids = Vec::new(); for i in 0..flow_count { - let id = format!("rotation-flow-{:04}", i); + let id = format!("rotation-flow-{i:04}"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), diff --git a/src-tauri/src/flow_monitor/filter_parser.rs b/src-tauri/src/flow_monitor/filter_parser.rs index e511c7e08..645f37b3d 100644 --- a/src-tauri/src/flow_monitor/filter_parser.rs +++ b/src-tauri/src/flow_monitor/filter_parser.rs @@ -197,19 +197,19 @@ pub enum FilterToken { impl fmt::Display for FilterToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FilterToken::Model(s) => write!(f, "~m {}", s), - FilterToken::Provider(s) => write!(f, "~p {}", s), + FilterToken::Model(s) => write!(f, "~m {s}"), + FilterToken::Provider(s) => write!(f, "~p {s}"), FilterToken::State(s) => write!(f, "~s {}", state_to_string(s)), FilterToken::HasError => write!(f, "~e"), FilterToken::HasToolCalls => write!(f, "~t"), FilterToken::HasThinking => write!(f, "~k"), FilterToken::Starred => write!(f, "~starred"), - FilterToken::Tag(s) => write!(f, "~tag {}", s), - FilterToken::Body(s) => write!(f, "~b {}", s), - FilterToken::BodyRequest(s) => write!(f, "~bq {}", s), - FilterToken::BodyResponse(s) => write!(f, "~bs {}", s), - FilterToken::Tokens(c) => write!(f, "~tokens {}", c), - FilterToken::Latency(c) => write!(f, "~latency {}", c), + FilterToken::Tag(s) => write!(f, "~tag {s}"), + FilterToken::Body(s) => write!(f, "~b {s}"), + FilterToken::BodyRequest(s) => write!(f, "~bq {s}"), + FilterToken::BodyResponse(s) => write!(f, "~bs {s}"), + FilterToken::Tokens(c) => write!(f, "~tokens {c}"), + FilterToken::Latency(c) => write!(f, "~latency {c}"), FilterToken::And => write!(f, "&"), FilterToken::Or => write!(f, "|"), FilterToken::Not => write!(f, "!"), @@ -262,10 +262,10 @@ pub enum FilterExpr { impl fmt::Display for FilterExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FilterExpr::Token(t) => write!(f, "{}", t), - FilterExpr::And(left, right) => write!(f, "({} & {})", left, right), - FilterExpr::Or(left, right) => write!(f, "({} | {})", left, right), - FilterExpr::Not(expr) => write!(f, "!{}", expr), + FilterExpr::Token(t) => write!(f, "{t}"), + FilterExpr::And(left, right) => write!(f, "({left} & {right})"), + FilterExpr::Or(left, right) => write!(f, "({left} | {right})"), + FilterExpr::Not(expr) => write!(f, "!{expr}"), } } } @@ -568,9 +568,8 @@ impl Parser { /// 检查当前 Token 是否匹配 fn check(&self, token: &FilterToken) -> bool { - self.peek().map_or(false, |t| { - std::mem::discriminant(t) == std::mem::discriminant(token) - }) + self.peek() + .is_some_and(|t| std::mem::discriminant(t) == std::mem::discriminant(token)) } /// 解析表达式 @@ -635,7 +634,7 @@ impl Parser { // 检查是否是过滤器 Token match token { FilterToken::And | FilterToken::Or | FilterToken::RightParen => Err( - FilterParseError::UnexpectedToken(format!("{}", token), self.pos), + FilterParseError::UnexpectedToken(format!("{token}"), self.pos), ), _ => { let token = self.advance().unwrap(); @@ -723,11 +722,10 @@ impl FilterParser { FilterToken::HasToolCalls => flow .response .as_ref() - .map_or(false, |r| !r.tool_calls.is_empty()), - FilterToken::HasThinking => flow - .response - .as_ref() - .map_or(false, |r| r.thinking.is_some()), + .is_some_and(|r| !r.tool_calls.is_empty()), + FilterToken::HasThinking => { + flow.response.as_ref().is_some_and(|r| r.thinking.is_some()) + } FilterToken::Starred => flow.annotations.starred, FilterToken::Tag(tag) => flow .annotations @@ -740,7 +738,7 @@ impl FilterParser { .response .as_ref() .map_or(String::new(), |r| r.content.clone()); - let combined = format!("{}\n{}", request_text, response_text); + let combined = format!("{request_text}\n{response_text}"); if let Ok(re) = Regex::new(pattern) { re.is_match(&combined) @@ -896,7 +894,7 @@ pub const FILTER_HELP: &[(&str, &str)] = &[ pub fn get_filter_help() -> String { let mut help = String::from("过滤表达式语法:\n\n"); for (syntax, desc) in FILTER_HELP { - help.push_str(&format!(" {:<20} {}\n", syntax, desc)); + help.push_str(&format!(" {syntax:<20} {desc}\n")); } help.push_str("\n示例:\n"); help.push_str(" ~m claude 模型名称包含 'claude'\n"); @@ -1237,7 +1235,7 @@ mod tests { #[test] fn test_display_filter_expr() { let expr = FilterParser::parse("~p kiro & ~m claude").unwrap(); - let display = format!("{}", expr); + let display = format!("{expr}"); assert!(display.contains("~p kiro")); assert!(display.contains("~m claude")); } @@ -1246,9 +1244,9 @@ mod tests { fn test_round_trip_simple() { let original = "~m claude"; let expr = FilterParser::parse(original).unwrap(); - let display = format!("{}", expr); + let display = format!("{expr}"); let reparsed = FilterParser::parse(&display).unwrap(); - assert_eq!(format!("{}", expr), format!("{}", reparsed)); + assert_eq!(format!("{expr}"), format!("{}", reparsed)); } } @@ -1419,7 +1417,7 @@ mod property_tests { ) { // 测试模型过滤器正确性 let model = flow.request.model.clone(); - let expr_str = format!("~m {}", model); + let expr_str = format!("~m {model}"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); @@ -1438,7 +1436,7 @@ mod property_tests { ) { // 测试提供商过滤器正确性 let provider_str = format!("{:?}", flow.metadata.provider).to_lowercase(); - let expr_str = format!("~p {}", provider_str); + let expr_str = format!("~p {provider_str}"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); @@ -1456,7 +1454,7 @@ mod property_tests { ) { // 测试状态过滤器正确性 let state_str = state_to_string(&flow.state); - let expr_str = format!("~s {}", state_str); + let expr_str = format!("~s {state_str}"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); @@ -1496,7 +1494,7 @@ mod property_tests { let has_tool_calls = flow .response .as_ref() - .map_or(false, |r| !r.tool_calls.is_empty()); + .is_some_and(|r| !r.tool_calls.is_empty()); prop_assert_eq!( result, @@ -1517,7 +1515,7 @@ mod property_tests { let has_thinking = flow .response .as_ref() - .map_or(false, |r| r.thinking.is_some()); + .is_some_and(|r| r.thinking.is_some()); prop_assert_eq!( result, @@ -1554,7 +1552,7 @@ mod property_tests { .map_or(0, |r| r.usage.total_tokens as i64); // 测试大于 - let expr_str = format!("~tokens >{}", threshold); + let expr_str = format!("~tokens >{threshold}"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); let result = filter(&flow); @@ -1578,7 +1576,7 @@ mod property_tests { let duration_ms = flow.timestamps.duration_ms as i64; // 测试大于 - let expr_str = format!("~latency >{}ms", threshold); + let expr_str = format!("~latency >{threshold}ms"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); let result = filter(&flow); @@ -1601,7 +1599,7 @@ mod property_tests { let model = flow.request.model.clone(); let provider_str = format!("{:?}", flow.metadata.provider).to_lowercase(); - let expr_str = format!("~m {} & ~p {}", model, provider_str); + let expr_str = format!("~m {model} & ~p {provider_str}"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); @@ -1621,7 +1619,7 @@ mod property_tests { let model = flow.request.model.clone(); // 使用一个匹配的条件和一个不匹配的条件 - let expr_str = format!("~m {} | ~m nonexistent-model-xyz", model); + let expr_str = format!("~m {model} | ~m nonexistent-model-xyz"); let expr = FilterParser::parse(&expr_str).unwrap(); let filter = FilterParser::compile(&expr); @@ -1725,7 +1723,7 @@ mod property_tests { flow in arb_llm_flow(), ) { // 序列化为字符串 - let expr_str = format!("{}", expr); + let expr_str = format!("{expr}"); // 重新解析 let reparsed = FilterParser::parse(&expr_str); @@ -1761,7 +1759,7 @@ mod property_tests { flow in arb_llm_flow(), ) { let expr = FilterExpr::Token(token); - let expr_str = format!("{}", expr); + let expr_str = format!("{expr}"); // 重新解析 let reparsed = FilterParser::parse(&expr_str); @@ -1844,7 +1842,7 @@ mod property_tests { fn prop_invalid_filter_returns_error( filter_name in arb_invalid_filter_name(), ) { - let expr_str = format!("~{}", filter_name); + let expr_str = format!("~{filter_name}"); let result = FilterParser::parse(&expr_str); // 应该返回错误 @@ -1868,7 +1866,7 @@ mod property_tests { fn prop_invalid_state_returns_error( state in arb_invalid_state(), ) { - let expr_str = format!("~s {}", state); + let expr_str = format!("~s {state}"); let result = FilterParser::parse(&expr_str); // 应该返回错误 @@ -1892,7 +1890,7 @@ mod property_tests { fn prop_invalid_comparison_op_returns_error( op in arb_invalid_comparison_op(), ) { - let expr_str = format!("~tokens {}100", op); + let expr_str = format!("~tokens {op}100"); let result = FilterParser::parse(&expr_str); // 应该返回错误 @@ -1909,7 +1907,7 @@ mod property_tests { ) { // 生成不匹配的括号 let open_parens: String = (0..depth).map(|_| '(').collect(); - let expr_str = format!("{}~e", open_parens); + let expr_str = format!("{open_parens}~e"); let result = FilterParser::parse(&expr_str); // 应该返回错误 @@ -1955,7 +1953,7 @@ mod property_tests { ], ) { // 缺少参数的过滤器 - let expr_str = format!("~{}", filter); + let expr_str = format!("~{filter}"); let result = FilterParser::parse(&expr_str); // 应该返回错误(缺少参数) diff --git a/src-tauri/src/flow_monitor/interceptor.rs b/src-tauri/src/flow_monitor/interceptor.rs index 0c219001b..27d1620ee 100644 --- a/src-tauri/src/flow_monitor/interceptor.rs +++ b/src-tauri/src/flow_monitor/interceptor.rs @@ -26,19 +26,15 @@ use super::models::{LLMFlow, LLMRequest, LLMResponse}; /// 超时动作 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum TimeoutAction { /// 超时后继续处理 + #[default] Continue, /// 超时后取消请求 Cancel, } -impl Default for TimeoutAction { - fn default() -> Self { - TimeoutAction::Continue - } -} - /// 拦截配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InterceptConfig { @@ -100,8 +96,10 @@ pub enum InterceptType { /// 拦截状态 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum InterceptState { /// 等待用户操作 + #[default] Pending, /// 用户正在编辑 Editing, @@ -113,12 +111,6 @@ pub enum InterceptState { TimedOut, } -impl Default for InterceptState { - fn default() -> Self { - InterceptState::Pending - } -} - // ============================================================================ // 被拦截的 Flow // ============================================================================ @@ -1158,20 +1150,20 @@ mod property_tests { /// 生成随机的过滤表达式 fn arb_filter_expr() -> impl Strategy { prop_oneof![ - arb_model_name().prop_map(|m| format!("~m {}", m)), + arb_model_name().prop_map(|m| format!("~m {m}")), prop_oneof![ Just("kiro".to_string()), Just("openai".to_string()), Just("claude".to_string()), Just("gemini".to_string()), ] - .prop_map(|p| format!("~p {}", p)), + .prop_map(|p| format!("~p {p}")), Just("~e".to_string()), Just("~t".to_string()), Just("~k".to_string()), Just("~starred".to_string()), - (0i64..50000i64).prop_map(|n| format!("~tokens >{}", n)), - (0i64..30000i64).prop_map(|n| format!("~latency >{}ms", n)), + (0i64..50000i64).prop_map(|n| format!("~tokens >{n}")), + (0i64..30000i64).prop_map(|n| format!("~latency >{n}ms")), ] } @@ -1303,7 +1295,7 @@ mod property_tests { // 使用模型过滤器 let config = InterceptConfig { enabled: true, - filter_expr: Some(format!("~m {}", model)), + filter_expr: Some(format!("~m {model}")), intercept_request: true, ..Default::default() }; @@ -1333,7 +1325,7 @@ mod property_tests { // 使用提供商过滤器 let config = InterceptConfig { enabled: true, - filter_expr: Some(format!("~p {}", provider_str)), + filter_expr: Some(format!("~p {provider_str}")), intercept_request: true, ..Default::default() }; @@ -1422,7 +1414,7 @@ mod property_tests { // 使用 Token 过滤器 let config = InterceptConfig { enabled: true, - filter_expr: Some(format!("~tokens >{}", threshold)), + filter_expr: Some(format!("~tokens >{threshold}")), intercept_request: true, ..Default::default() }; @@ -1454,7 +1446,7 @@ mod property_tests { // 使用延迟过滤器 let config = InterceptConfig { enabled: true, - filter_expr: Some(format!("~latency >{}ms", threshold)), + filter_expr: Some(format!("~latency >{threshold}ms")), intercept_request: true, ..Default::default() }; diff --git a/src-tauri/src/flow_monitor/memory_store.rs b/src-tauri/src/flow_monitor/memory_store.rs index f2d0f89ed..7b3438553 100644 --- a/src-tauri/src/flow_monitor/memory_store.rs +++ b/src-tauri/src/flow_monitor/memory_store.rs @@ -32,8 +32,8 @@ impl TimeRange { /// 检查时间是否在范围内 pub fn contains(&self, time: &DateTime) -> bool { - let after_start = self.start.map_or(true, |s| time >= &s); - let before_end = self.end.map_or(true, |e| time <= &e); + let after_start = self.start.is_none_or(|s| time >= &s); + let before_end = self.end.is_none_or(|e| time <= &e); after_start && before_end } } @@ -50,8 +50,8 @@ pub struct TokenRange { impl TokenRange { /// 检查 Token 数是否在范围内 pub fn contains(&self, tokens: u32) -> bool { - let above_min = self.min.map_or(true, |m| tokens >= m); - let below_max = self.max.map_or(true, |m| tokens <= m); + let above_min = self.min.is_none_or(|m| tokens >= m); + let below_max = self.max.is_none_or(|m| tokens <= m); above_min && below_max } } @@ -68,8 +68,8 @@ pub struct LatencyRange { impl LatencyRange { /// 检查延迟是否在范围内 pub fn contains(&self, latency_ms: u64) -> bool { - let above_min = self.min_ms.map_or(true, |m| latency_ms >= m); - let below_max = self.max_ms.map_or(true, |m| latency_ms <= m); + let above_min = self.min_ms.is_none_or(|m| latency_ms >= m); + let below_max = self.max_ms.is_none_or(|m| latency_ms <= m); above_min && below_max } } @@ -181,7 +181,7 @@ impl FlowFilter { let flow_has_tool_calls = flow .response .as_ref() - .map_or(false, |r| !r.tool_calls.is_empty()); + .is_some_and(|r| !r.tool_calls.is_empty()); if has_tool_calls != flow_has_tool_calls { return false; } @@ -189,10 +189,7 @@ impl FlowFilter { // 思维链过滤 if let Some(has_thinking) = self.has_thinking { - let flow_has_thinking = flow - .response - .as_ref() - .map_or(false, |r| r.thinking.is_some()); + let flow_has_thinking = flow.response.as_ref().is_some_and(|r| r.thinking.is_some()); if has_thinking != flow_has_thinking { return false; } @@ -402,7 +399,7 @@ impl FlowMemoryStore { // 如果已存在,先移除旧的 if self.flows.contains_key(&id) { self.ordered_ids.retain(|i| i != &id); - eprintln!("[MEMORY_STORE] 移除旧的 Flow: id={}", id); + eprintln!("[MEMORY_STORE] 移除旧的 Flow: id={id}"); } // 检查是否需要驱逐 @@ -968,7 +965,7 @@ mod property_tests { // 添加多个 Flow for i in 0..flow_count { - let id = format!("flow-{}", i); + let id = format!("flow-{i}"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), @@ -1011,7 +1008,7 @@ mod property_tests { // 添加 max_size + 1 个 Flow let total_flows = max_size + 1; for i in 0..total_flows { - let id = format!("flow-{}", i); + let id = format!("flow-{i}"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), @@ -1032,7 +1029,7 @@ mod property_tests { // 验证最新的 Flow 仍然存在 for i in 1..total_flows { prop_assert!( - store.contains(&format!("flow-{}", i)), + store.contains(&format!("flow-{i}")), "Flow flow-{} 应该仍然存在", i ); @@ -1087,15 +1084,13 @@ mod property_tests { let mut store = FlowMemoryStore::new(100); // 添加不同 Provider 的 Flow - let providers = vec![ - ProviderType::OpenAI, + let providers = [ProviderType::OpenAI, ProviderType::Claude, ProviderType::Gemini, - ProviderType::Kiro, - ]; + ProviderType::Kiro]; for (i, p) in providers.iter().enumerate() { - let id = format!("flow-{}", i); + let id = format!("flow-{i}"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), @@ -1103,7 +1098,7 @@ mod property_tests { ..Default::default() }; let metadata = FlowMetadata { - provider: p.clone(), + provider: *p, ..Default::default() }; let flow = LLMFlow::new(id, FlowType::ChatCompletions, request, metadata); @@ -1112,7 +1107,7 @@ mod property_tests { // 按 Provider 过滤 let filter = FlowFilter { - providers: Some(vec![provider.clone()]), + providers: Some(vec![provider]), ..Default::default() }; @@ -1140,15 +1135,13 @@ mod property_tests { // 添加不同模型的 Flow // 使用数字前缀的模型名称,确保不会与随机生成的字母 prefix 冲突 - let models = vec![ - format!("{}-model-1", prefix), - format!("{}-model-2", prefix), + let models = [format!("{prefix}-model-1"), + format!("{prefix}-model-2"), "123-non-matching-model".to_string(), - "456-another-non-matching".to_string(), - ]; + "456-another-non-matching".to_string()]; for (i, model) in models.iter().enumerate() { - let id = format!("flow-{}", i); + let id = format!("flow-{i}"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), @@ -1161,7 +1154,7 @@ mod property_tests { } // 使用通配符过滤 - let pattern = format!("{}*", prefix); + let pattern = format!("{prefix}*"); let filter = FlowFilter { models: Some(vec![pattern.clone()]), ..Default::default() @@ -1233,7 +1226,7 @@ mod property_tests { // 添加多个 Flow for i in 0..count { - let id = format!("flow-{:03}", i); + let id = format!("flow-{i:03}"); let request = LLMRequest::default(); let metadata = FlowMetadata::default(); let flow = LLMFlow::new(id, FlowType::ChatCompletions, request, metadata); diff --git a/src-tauri/src/flow_monitor/models.rs b/src-tauri/src/flow_monitor/models.rs index 89c7dd24b..0d4888db4 100644 --- a/src-tauri/src/flow_monitor/models.rs +++ b/src-tauri/src/flow_monitor/models.rs @@ -70,9 +70,10 @@ impl LLMFlow { } /// 流类型 -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum FlowType { /// OpenAI Chat Completions + #[default] ChatCompletions, /// Anthropic Messages AnthropicMessages, @@ -84,16 +85,11 @@ pub enum FlowType { Other(String), } -impl Default for FlowType { - fn default() -> Self { - FlowType::ChatCompletions - } -} - /// 流状态 -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum FlowState { /// 等待响应 + #[default] Pending, /// 正在流式传输 Streaming, @@ -105,12 +101,6 @@ pub enum FlowState { Cancelled, } -impl Default for FlowState { - fn default() -> Self { - FlowState::Pending - } -} - // ============================================================================ // 请求数据结构 // ============================================================================ @@ -193,10 +183,12 @@ impl Default for Message { /// 消息角色 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum MessageRole { /// 系统消息 System, /// 用户消息 + #[default] User, /// 助手消息 Assistant, @@ -206,12 +198,6 @@ pub enum MessageRole { Function, } -impl Default for MessageRole { - fn default() -> Self { - MessageRole::User - } -} - /// 消息内容(支持多模态) #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -736,6 +722,7 @@ impl FlowError { /// 错误类型 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] +#[derive(Default)] pub enum FlowErrorType { /// 网络错误 Network, @@ -758,15 +745,10 @@ pub enum FlowErrorType { /// 请求被取消(用户拦截后取消) Cancelled, /// 其他错误 + #[default] Other, } -impl Default for FlowErrorType { - fn default() -> Self { - FlowErrorType::Other - } -} - impl FlowErrorType { /// 根据 HTTP 状态码推断错误类型 pub fn from_status_code(code: u16) -> Self { diff --git a/src-tauri/src/flow_monitor/monitor.rs b/src-tauri/src/flow_monitor/monitor.rs index 6195300af..ccbb2a108 100644 --- a/src-tauri/src/flow_monitor/monitor.rs +++ b/src-tauri/src/flow_monitor/monitor.rs @@ -248,7 +248,7 @@ impl Default for ThresholdConfig { /// 阈值检测结果 /// /// 表示 Flow 是否超过了配置的阈值。 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ThresholdCheckResult { /// 是否超过延迟阈值 pub latency_exceeded: bool, @@ -278,21 +278,6 @@ impl ThresholdCheckResult { } } -impl Default for ThresholdCheckResult { - fn default() -> Self { - Self { - latency_exceeded: false, - token_exceeded: false, - input_token_exceeded: false, - output_token_exceeded: false, - actual_latency_ms: 0, - actual_tokens: 0, - actual_input_tokens: 0, - actual_output_tokens: 0, - } - } -} - // ============================================================================ // 通知配置 // ============================================================================ @@ -337,7 +322,7 @@ pub struct NotificationConfig { } /// 通知设置 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct NotificationSettings { /// 是否启用 pub enabled: bool, @@ -380,17 +365,6 @@ fn default_token_warning() -> NotificationSettings { } } -impl Default for NotificationSettings { - fn default() -> Self { - Self { - enabled: false, - desktop: false, - sound: false, - sound_file: None, - } - } -} - impl Default for NotificationConfig { fn default() -> Self { Self { @@ -434,7 +408,7 @@ impl NotificationEvent { Self { notification_type: NotificationType::NewFlow, title: "新的 LLM 请求".to_string(), - message: format!("模型: {}", model), + message: format!("模型: {model}"), flow_id, timestamp: Utc::now(), desktop: settings.desktop, @@ -453,7 +427,7 @@ impl NotificationEvent { Self { notification_type: NotificationType::ErrorFlow, title: "LLM 请求失败".to_string(), - message: format!("模型: {}, 错误: {}", model, error), + message: format!("模型: {model}, 错误: {error}"), flow_id, timestamp: Utc::now(), desktop: settings.desktop, @@ -473,10 +447,7 @@ impl NotificationEvent { Self { notification_type: NotificationType::LatencyWarning, title: "延迟警告".to_string(), - message: format!( - "模型: {}, 延迟: {}ms (阈值: {}ms)", - model, actual_ms, threshold_ms - ), + message: format!("模型: {model}, 延迟: {actual_ms}ms (阈值: {threshold_ms}ms)"), flow_id, timestamp: Utc::now(), desktop: settings.desktop, @@ -496,10 +467,7 @@ impl NotificationEvent { Self { notification_type: NotificationType::TokenWarning, title: "Token 使用警告".to_string(), - message: format!( - "模型: {}, Token: {} (阈值: {})", - model, actual_tokens, threshold_tokens - ), + message: format!("模型: {model}, Token: {actual_tokens} (阈值: {threshold_tokens})"), flow_id, timestamp: Utc::now(), desktop: settings.desktop, @@ -673,11 +641,8 @@ impl From<&LLMFlow> for FlowSummary { has_tool_calls: flow .response .as_ref() - .map_or(false, |r| !r.tool_calls.is_empty()), - has_thinking: flow - .response - .as_ref() - .map_or(false, |r| r.thinking.is_some()), + .is_some_and(|r| !r.tool_calls.is_empty()), + has_thinking: flow.response.as_ref().is_some_and(|r| r.thinking.is_some()), } } } @@ -1230,9 +1195,9 @@ impl FlowMonitor { if let Some(ref file_store) = self.file_store { if let Err(e) = file_store.write(&active_flow.flow) { tracing::error!("保存 Flow 到文件失败: {}", e); - eprintln!("[FLOW_MONITOR] 保存到文件失败: id={}, error={}", flow_id, e); + eprintln!("[FLOW_MONITOR] 保存到文件失败: id={flow_id}, error={e}"); } else { - eprintln!("[FLOW_MONITOR] 已保存到文件存储: id={}", flow_id); + eprintln!("[FLOW_MONITOR] 已保存到文件存储: id={flow_id}"); } } else { eprintln!("[FLOW_MONITOR] 文件存储未启用"); @@ -1257,9 +1222,9 @@ impl FlowMonitor { .await; } - eprintln!("[FLOW_MONITOR] Flow 完成处理完毕: id={}", flow_id); + eprintln!("[FLOW_MONITOR] Flow 完成处理完毕: id={flow_id}"); } else { - eprintln!("[FLOW_MONITOR] 警告: 未找到活跃 Flow: id={}", flow_id); + eprintln!("[FLOW_MONITOR] 警告: 未找到活跃 Flow: id={flow_id}"); } } @@ -1480,10 +1445,10 @@ impl FlowMonitor { let token_exceeded = actual_tokens > config.token_threshold; let input_token_exceeded = config .input_token_threshold - .map_or(false, |threshold| actual_input_tokens > threshold); + .is_some_and(|threshold| actual_input_tokens > threshold); let output_token_exceeded = config .output_token_threshold - .map_or(false, |threshold| actual_output_tokens > threshold); + .is_some_and(|threshold| actual_output_tokens > threshold); ThresholdCheckResult { latency_exceeded, @@ -2121,7 +2086,7 @@ mod property_tests { // 测试排除模型配置 if let Some(ref excluded) = excluded_model { - let excluded_model_name = format!("{}-test", excluded); + let excluded_model_name = format!("{excluded}-test"); let request = LLMRequest { method: "POST".to_string(), path: "/v1/chat/completions".to_string(), @@ -2279,7 +2244,7 @@ mod property_tests { // 验证输入 Token 阈值检测 let expected_input_exceeded = input_token_threshold - .map_or(false, |threshold| actual_input_tokens > threshold); + .is_some_and(|threshold| actual_input_tokens > threshold); prop_assert_eq!( result.input_token_exceeded, expected_input_exceeded, @@ -2288,7 +2253,7 @@ mod property_tests { // 验证输出 Token 阈值检测 let expected_output_exceeded = output_token_threshold - .map_or(false, |threshold| actual_output_tokens > threshold); + .is_some_and(|threshold| actual_output_tokens > threshold); prop_assert_eq!( result.output_token_exceeded, expected_output_exceeded, diff --git a/src-tauri/src/flow_monitor/query_service.rs b/src-tauri/src/flow_monitor/query_service.rs index 8f4b137a2..f630591de 100644 --- a/src-tauri/src/flow_monitor/query_service.rs +++ b/src-tauri/src/flow_monitor/query_service.rs @@ -37,8 +37,10 @@ pub enum QueryWithExpressionError { /// 排序字段 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] +#[derive(Default)] pub enum FlowSortBy { /// 按创建时间排序 + #[default] CreatedAt, /// 按耗时排序 Duration, @@ -50,12 +52,6 @@ pub enum FlowSortBy { Model, } -impl Default for FlowSortBy { - fn default() -> Self { - FlowSortBy::CreatedAt - } -} - // ============================================================================ // 查询结果 // ============================================================================ @@ -213,8 +209,7 @@ impl FlowQueryService { page_size: usize, ) -> Result { eprintln!( - "[QUERY_SERVICE] 开始查询: filter={:?}, sort_by={:?}, page={}, page_size={}", - filter, sort_by, page, page_size + "[QUERY_SERVICE] 开始查询: filter={filter:?}, sort_by={sort_by:?}, page={page}, page_size={page_size}" ); // 先从内存获取 @@ -236,8 +231,7 @@ impl FlowQueryService { if memory_count < needed { eprintln!( - "[QUERY_SERVICE] 内存数据不足,从文件补充: memory_count={}, needed={}", - memory_count, needed + "[QUERY_SERVICE] 内存数据不足,从文件补充: memory_count={memory_count}, needed={needed}" ); // 从文件存储获取更多数据 @@ -263,7 +257,7 @@ impl FlowQueryService { // 计算分页 let total = all_flows.len(); let total_pages = if page_size > 0 { - (total + page_size - 1) / page_size + total.div_ceil(page_size) } else { 0 }; @@ -360,7 +354,7 @@ impl FlowQueryService { // 计算分页 let total = all_flows.len(); let total_pages = if page_size > 0 { - (total + page_size - 1) / page_size + total.div_ceil(page_size) } else { 0 }; @@ -1142,12 +1136,10 @@ mod property_tests { provider in arb_provider_type(), ) { // 创建不同 Provider 的 Flow - let providers = vec![ - ProviderType::OpenAI, + let providers = [ProviderType::OpenAI, ProviderType::Claude, ProviderType::Gemini, - ProviderType::Kiro, - ]; + ProviderType::Kiro]; let mut flows: Vec = Vec::new(); for (i, p) in providers.iter().enumerate() { @@ -1158,16 +1150,16 @@ mod property_tests { ..Default::default() }; let metadata = FlowMetadata { - provider: p.clone(), + provider: *p, ..Default::default() }; - let flow = LLMFlow::new(format!("flow-{}", i), FlowType::ChatCompletions, request, metadata); + let flow = LLMFlow::new(format!("flow-{i}"), FlowType::ChatCompletions, request, metadata); flows.push(flow); } // 按 Provider 过滤 let filter = FlowFilter { - providers: Some(vec![provider.clone()]), + providers: Some(vec![provider]), ..Default::default() }; @@ -1202,7 +1194,7 @@ mod property_tests { ..Default::default() }; let metadata = FlowMetadata::default(); - let mut flow = LLMFlow::new(format!("flow-{}", i), FlowType::ChatCompletions, request, metadata); + let mut flow = LLMFlow::new(format!("flow-{i}"), FlowType::ChatCompletions, request, metadata); flow.timestamps.duration_ms = (i * 100) as u64; flow.timestamps.created = Utc::now() - chrono::Duration::minutes(i as i64); @@ -1281,14 +1273,14 @@ mod property_tests { ..Default::default() }; let metadata = FlowMetadata::default(); - let flow = LLMFlow::new(format!("flow-{:04}", i), FlowType::ChatCompletions, request, metadata); + let flow = LLMFlow::new(format!("flow-{i:04}"), FlowType::ChatCompletions, request, metadata); all_flows.push(flow); } // 计算分页 let total = all_flows.len(); let total_pages = if page_size > 0 { - (total + page_size - 1) / page_size + total.div_ceil(page_size) } else { 0 }; diff --git a/src-tauri/src/flow_monitor/quick_filter.rs b/src-tauri/src/flow_monitor/quick_filter.rs index 9f0a50d86..9f337a96d 100644 --- a/src-tauri/src/flow_monitor/quick_filter.rs +++ b/src-tauri/src/flow_monitor/quick_filter.rs @@ -1073,7 +1073,7 @@ mod tests { // 验证预设过滤器内容 for (name, expr, _) in PRESET_FILTERS { let filter = manager.find_by_name(name).unwrap(); - assert!(filter.is_some(), "Preset filter '{}' should exist", name); + assert!(filter.is_some(), "Preset filter '{name}' should exist"); let filter = filter.unwrap(); assert_eq!(filter.filter_expr, *expr); assert!(filter.is_preset); @@ -1138,9 +1138,9 @@ mod property_tests { Just("~e | ~t".to_string()), Just("~e & ~t".to_string()), Just("!~e".to_string()), - "[a-zA-Z0-9_-]{1,20}".prop_map(|s| format!("~m {}", s)), - "[a-zA-Z0-9_-]{1,20}".prop_map(|s| format!("~p {}", s)), - "[a-zA-Z0-9_-]{1,20}".prop_map(|s| format!("~tag {}", s)), + "[a-zA-Z0-9_-]{1,20}".prop_map(|s| format!("~m {s}")), + "[a-zA-Z0-9_-]{1,20}".prop_map(|s| format!("~p {s}")), + "[a-zA-Z0-9_-]{1,20}".prop_map(|s| format!("~tag {s}")), ] } @@ -1294,7 +1294,7 @@ mod property_tests { // 保存所有过滤器 let mut saved_ids = Vec::new(); for (i, (name, filter_expr, description, group)) in filters.iter().enumerate() { - let unique_name = format!("{}_{}", name, i); + let unique_name = format!("{name}_{i}"); let filter = manager.save(&unique_name, filter_expr, description.as_deref(), group.as_deref()).unwrap(); saved_ids.push(filter.id); } diff --git a/src-tauri/src/flow_monitor/replayer.rs b/src-tauri/src/flow_monitor/replayer.rs index 2ee2a016f..ffe5142c5 100644 --- a/src-tauri/src/flow_monitor/replayer.rs +++ b/src-tauri/src/flow_monitor/replayer.rs @@ -585,7 +585,7 @@ impl FlowReplayer { id.clone() } else { // 尝试从凭证池选择 - let provider_type_str = format!("{:?}", provider); + let provider_type_str = format!("{provider:?}"); if let Ok(Some(cred)) = self.provider_pool .select_credential(&self.db, &provider_type_str, None) @@ -946,7 +946,7 @@ mod property_tests { state: FlowState::Pending, annotations: FlowAnnotations { marker: Some("🔄".to_string()), // 重放标记 - comment: Some(format!("重放自 Flow: {}", original_flow_id)), + comment: Some(format!("重放自 Flow: {original_flow_id}")), tags: vec!["replay".to_string()], starred: false, }, diff --git a/src-tauri/src/flow_monitor/session.rs b/src-tauri/src/flow_monitor/session.rs index 39ca90b0b..d11058db2 100644 --- a/src-tauri/src/flow_monitor/session.rs +++ b/src-tauri/src/flow_monitor/session.rs @@ -764,7 +764,7 @@ impl SessionManager { flows.len() ); if let Some(ref desc) = session.description { - md.push_str(&format!("**描述**: {}\n\n", desc)); + md.push_str(&format!("**描述**: {desc}\n\n")); } md.push_str("---\n\n"); md.push_str(&exporter.export_markdown_multiple(flows)); @@ -969,7 +969,7 @@ mod tests { let mut ids = std::collections::HashSet::new(); for i in 0..100 { let session = manager - .create_session(format!("Session {}", i), None) + .create_session(format!("Session {i}"), None) .unwrap(); assert!(ids.insert(session.id), "Session ID should be unique"); } diff --git a/src-tauri/src/flow_monitor/stream_rebuilder.rs b/src-tauri/src/flow_monitor/stream_rebuilder.rs index 8be749b38..0ab23e4c6 100644 --- a/src-tauri/src/flow_monitor/stream_rebuilder.rs +++ b/src-tauri/src/flow_monitor/stream_rebuilder.rs @@ -38,7 +38,7 @@ pub enum StreamRebuilderError { // ============================================================================ /// 流式响应格式 -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum StreamFormat { /// OpenAI 格式 (data: {...}) OpenAI, @@ -47,15 +47,10 @@ pub enum StreamFormat { /// Gemini 格式 Gemini, /// 未知格式 + #[default] Unknown, } -impl Default for StreamFormat { - fn default() -> Self { - StreamFormat::Unknown - } -} - // ============================================================================ // 工具调用构建器 // ============================================================================ @@ -313,10 +308,7 @@ impl StreamRebuilder { ) -> Result<(), StreamRebuilderError> { let index = tc.get("index").and_then(|v| v.as_u64()).unwrap_or(0) as u32; - let builder = self - .tool_calls_buffer - .entry(index) - .or_insert_with(ToolCallBuilder::new); + let builder = self.tool_calls_buffer.entry(index).or_default(); // 提取 ID if let Some(id) = tc.get("id").and_then(|v| v.as_str()) { @@ -493,10 +485,7 @@ impl StreamRebuilder { match block_type { "tool_use" => { // 工具调用开始 - let builder = self - .tool_calls_buffer - .entry(index) - .or_insert_with(ToolCallBuilder::new); + let builder = self.tool_calls_buffer.entry(index).or_default(); builder.id = content_block .get("id") .and_then(|v| v.as_str()) @@ -683,10 +672,7 @@ impl StreamRebuilder { chunk: &mut StreamChunk, ) -> Result<(), StreamRebuilderError> { let index = self.tool_calls_buffer.len() as u32; - let builder = self - .tool_calls_buffer - .entry(index) - .or_insert_with(ToolCallBuilder::new); + let builder = self.tool_calls_buffer.entry(index).or_default(); // Gemini 的函数调用通常是完整的,不是增量的 if let Some(name) = function_call.get("name").and_then(|v| v.as_str()) { @@ -868,7 +854,7 @@ impl StreamRebuilder { "choices": [{ "index": 0, "message": message, - "finish_reason": self.stop_reason.as_ref().map(|r| format!("{:?}", r).to_lowercase()), + "finish_reason": self.stop_reason.as_ref().map(|r| format!("{r:?}").to_lowercase()), }], "usage": { "prompt_tokens": self.usage.input_tokens, @@ -1297,9 +1283,9 @@ mod property_tests { prop::option::of("[a-zA-Z0-9_]{1,20}"), // argument value ) .prop_map(|(name, id_suffix, arg_value)| { - let id = format!("call_{}", id_suffix); + let id = format!("call_{id_suffix}"); let args = match arg_value { - Some(val) => format!(r#"{{"value":"{}"}}"#, val), + Some(val) => format!(r#"{{"value":"{val}"}}"#), None => "{}".to_string(), }; (id, name, args) @@ -1317,8 +1303,7 @@ mod property_tests { // 初始 chunk chunks.push(format!( - r#"{{"id":"{}","object":"chat.completion.chunk","created":1234567890,"model":"{}","choices":[{{"index":0,"delta":{{"role":"assistant","content":""}},"finish_reason":null}}]}}"#, - id, model + r#"{{"id":"{id}","object":"chat.completion.chunk","created":1234567890,"model":"{model}","choices":[{{"index":0,"delta":{{"role":"assistant","content":""}},"finish_reason":null}}]}}"# )); // 内容 chunks(每个字符一个 chunk) @@ -1330,8 +1315,7 @@ mod property_tests { _ => ch.to_string(), }; chunks.push(format!( - r#"{{"id":"{}","object":"chat.completion.chunk","created":1234567890,"model":"{}","choices":[{{"index":0,"delta":{{"content":"{}"}},"finish_reason":null}}]}}"#, - id, model, escaped + r#"{{"id":"{id}","object":"chat.completion.chunk","created":1234567890,"model":"{model}","choices":[{{"index":0,"delta":{{"content":"{escaped}"}},"finish_reason":null}}]}}"# )); } @@ -1339,15 +1323,13 @@ mod property_tests { for (idx, (call_id, name, args)) in tool_calls.iter().enumerate() { // 工具调用开始 chunks.push(format!( - r#"{{"id":"{}","object":"chat.completion.chunk","created":1234567890,"model":"{}","choices":[{{"index":0,"delta":{{"tool_calls":[{{"index":{},"id":"{}","type":"function","function":{{"name":"{}","arguments":""}}}}]}},"finish_reason":null}}]}}"#, - id, model, idx, call_id, name + r#"{{"id":"{id}","object":"chat.completion.chunk","created":1234567890,"model":"{model}","choices":[{{"index":0,"delta":{{"tool_calls":[{{"index":{idx},"id":"{call_id}","type":"function","function":{{"name":"{name}","arguments":""}}}}]}},"finish_reason":null}}]}}"# )); // 工具调用参数(一次性发送,避免分块导致的转义问题) let args_escaped = args.replace('\\', "\\\\").replace('"', "\\\""); chunks.push(format!( - r#"{{"id":"{}","object":"chat.completion.chunk","created":1234567890,"model":"{}","choices":[{{"index":0,"delta":{{"tool_calls":[{{"index":{},"function":{{"arguments":"{}"}}}}]}},"finish_reason":null}}]}}"#, - id, model, idx, args_escaped + r#"{{"id":"{id}","object":"chat.completion.chunk","created":1234567890,"model":"{model}","choices":[{{"index":0,"delta":{{"tool_calls":[{{"index":{idx},"function":{{"arguments":"{args_escaped}"}}}}]}},"finish_reason":null}}]}}"# )); } @@ -1358,8 +1340,7 @@ mod property_tests { "tool_calls" }; chunks.push(format!( - r#"{{"id":"{}","object":"chat.completion.chunk","created":1234567890,"model":"{}","choices":[{{"index":0,"delta":{{}},"finish_reason":"{}"}}]}}"#, - id, model, finish_reason + r#"{{"id":"{id}","object":"chat.completion.chunk","created":1234567890,"model":"{model}","choices":[{{"index":0,"delta":{{}},"finish_reason":"{finish_reason}"}}]}}"# )); // [DONE] 信号 @@ -1381,8 +1362,7 @@ mod property_tests { events.push(( "message_start".to_string(), format!( - r#"{{"type":"message_start","message":{{"id":"{}","type":"message","role":"assistant","model":"{}","usage":{{"input_tokens":10}}}}}}"#, - id, model + r#"{{"type":"message_start","message":{{"id":"{id}","type":"message","role":"assistant","model":"{model}","usage":{{"input_tokens":10}}}}}}"# ), )); @@ -1404,8 +1384,7 @@ mod property_tests { events.push(( "content_block_delta".to_string(), format!( - r#"{{"type":"content_block_delta","index":0,"delta":{{"type":"text_delta","text":"{}"}}}}"#, - escaped + r#"{{"type":"content_block_delta","index":0,"delta":{{"type":"text_delta","text":"{escaped}"}}}}"# ), )); } @@ -1423,8 +1402,7 @@ mod property_tests { events.push(( "content_block_start".to_string(), format!( - r#"{{"type":"content_block_start","index":{},"content_block":{{"type":"tool_use","id":"{}","name":"{}"}}}}"#, - block_idx, call_id, name + r#"{{"type":"content_block_start","index":{block_idx},"content_block":{{"type":"tool_use","id":"{call_id}","name":"{name}"}}}}"# ), )); @@ -1433,14 +1411,13 @@ mod property_tests { events.push(( "content_block_delta".to_string(), format!( - r#"{{"type":"content_block_delta","index":{},"delta":{{"type":"input_json_delta","partial_json":"{}"}}}}"#, - block_idx, args_escaped + r#"{{"type":"content_block_delta","index":{block_idx},"delta":{{"type":"input_json_delta","partial_json":"{args_escaped}"}}}}"# ), )); events.push(( "content_block_stop".to_string(), - format!(r#"{{"type":"content_block_stop","index":{}}}"#, block_idx), + format!(r#"{{"type":"content_block_stop","index":{block_idx}}}"#), )); } @@ -1453,8 +1430,7 @@ mod property_tests { events.push(( "message_delta".to_string(), format!( - r#"{{"type":"message_delta","delta":{{"stop_reason":"{}"}},"usage":{{"output_tokens":20}}}}"#, - stop_reason + r#"{{"type":"message_delta","delta":{{"stop_reason":"{stop_reason}"}},"usage":{{"output_tokens":20}}}}"# ), )); @@ -1479,8 +1455,7 @@ mod property_tests { .replace('"', "\\\"") .replace('\n', "\\n"); chunks.push(format!( - r#"{{"candidates":[{{"content":{{"parts":[{{"text":"{}"}}],"role":"model"}},"index":0}}]}}"#, - escaped + r#"{{"candidates":[{{"content":{{"parts":[{{"text":"{escaped}"}}],"role":"model"}},"index":0}}]}}"# )); } diff --git a/src-tauri/src/memory/manager.rs b/src-tauri/src/memory/manager.rs index b3b68ae2f..7c36242c7 100644 --- a/src-tauri/src/memory/manager.rs +++ b/src-tauri/src/memory/manager.rs @@ -48,10 +48,7 @@ impl MemoryManager { updated_at: now, }; - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; conn.execute( "INSERT INTO characters (id, project_id, name, aliases_json, description, personality, background, appearance, relationships_json, avatar_url, is_main, sort_order, extra_json, created_at, updated_at) @@ -74,7 +71,7 @@ impl MemoryManager { character.updated_at.timestamp_millis(), ], ) - .map_err(|e| format!("创建角色失败: {}", e))?; + .map_err(|e| format!("创建角色失败: {e}"))?; tracing::info!( "[Memory] 创建角色: id={}, name={}", @@ -86,44 +83,38 @@ impl MemoryManager { /// 获取角色 pub fn get_character(&self, id: &CharacterId) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT id, project_id, name, aliases_json, description, personality, background, appearance, relationships_json, avatar_url, is_main, sort_order, extra_json, created_at, updated_at FROM characters WHERE id = ?", params![id], - |row| Ok(Self::row_to_character(row)?), + Self::row_to_character, ); match result { Ok(character) => Ok(Some(character)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取角色失败: {}", e)), + Err(e) => Err(format!("获取角色失败: {e}")), } } /// 列出项目的所有角色 pub fn list_characters(&self, project_id: &str) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( "SELECT id, project_id, name, aliases_json, description, personality, background, appearance, relationships_json, avatar_url, is_main, sort_order, extra_json, created_at, updated_at FROM characters WHERE project_id = ? ORDER BY sort_order ASC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let characters = stmt - .query_map(params![project_id], |row| Ok(Self::row_to_character(row)?)) - .map_err(|e| format!("查询失败: {}", e))? + .query_map(params![project_id], Self::row_to_character) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(characters) } @@ -134,10 +125,7 @@ impl MemoryManager { id: &CharacterId, updates: CharacterUpdateRequest, ) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let now = Utc::now().timestamp_millis(); let mut set_clauses = vec!["updated_at = ?"]; @@ -200,7 +188,7 @@ impl MemoryManager { params_vec.iter().map(|p| p.as_ref()).collect(); conn.execute(&sql, params_refs.as_slice()) - .map_err(|e| format!("更新角色失败: {}", e))?; + .map_err(|e| format!("更新角色失败: {e}"))?; drop(conn); self.get_character(id)? @@ -209,14 +197,11 @@ impl MemoryManager { /// 删除角色 pub fn delete_character(&self, id: &CharacterId) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let affected = conn .execute("DELETE FROM characters WHERE id = ?", params![id]) - .map_err(|e| format!("删除角色失败: {}", e))?; + .map_err(|e| format!("删除角色失败: {e}"))?; if affected > 0 { tracing::info!("[Memory] 删除角色: id={}", id); @@ -225,16 +210,13 @@ impl MemoryManager { } fn get_next_character_order(&self, project_id: &str) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result: Result = conn.query_row( "SELECT COALESCE(MAX(sort_order), -1) + 1 FROM characters WHERE project_id = ?", params![project_id], |row| row.get(0), ); - result.map_err(|e| format!("获取排序顺序失败: {}", e)) + result.map_err(|e| format!("获取排序顺序失败: {e}")) } fn row_to_character(row: &rusqlite::Row) -> Result { @@ -279,10 +261,7 @@ impl MemoryManager { /// 获取或创建世界观 pub fn get_world_building(&self, project_id: &str) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT project_id, description, era, locations, rules, extra_json, updated_at @@ -313,7 +292,7 @@ impl MemoryManager { match result { Ok(wb) => Ok(Some(wb)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取世界观失败: {}", e)), + Err(e) => Err(format!("获取世界观失败: {e}")), } } @@ -323,10 +302,7 @@ impl MemoryManager { project_id: &str, updates: WorldBuildingUpdateRequest, ) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let now = Utc::now(); let extra_json = updates @@ -354,7 +330,7 @@ impl MemoryManager { now.timestamp_millis(), ], ) - .map_err(|e| format!("更新世界观失败: {}", e))?; + .map_err(|e| format!("更新世界观失败: {e}"))?; drop(conn); self.get_world_building(project_id)? @@ -365,10 +341,7 @@ impl MemoryManager { /// 获取风格指南 pub fn get_style_guide(&self, project_id: &str) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT project_id, style, tone, forbidden_words_json, preferred_words_json, examples, extra_json, updated_at @@ -400,7 +373,7 @@ impl MemoryManager { match result { Ok(sg) => Ok(Some(sg)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取风格指南失败: {}", e)), + Err(e) => Err(format!("获取风格指南失败: {e}")), } } @@ -410,10 +383,7 @@ impl MemoryManager { project_id: &str, updates: StyleGuideUpdateRequest, ) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let now = Utc::now(); let forbidden_words_json = updates @@ -453,7 +423,7 @@ impl MemoryManager { now.timestamp_millis(), ], ) - .map_err(|e| format!("更新风格指南失败: {}", e))?; + .map_err(|e| format!("更新风格指南失败: {e}"))?; drop(conn); self.get_style_guide(project_id)? @@ -488,10 +458,7 @@ impl MemoryManager { updated_at: now, }; - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; conn.execute( "INSERT INTO outline_nodes (id, project_id, parent_id, title, content, content_id, sort_order, expanded, extra_json, created_at, updated_at) @@ -510,7 +477,7 @@ impl MemoryManager { node.updated_at.timestamp_millis(), ], ) - .map_err(|e| format!("创建大纲节点失败: {}", e))?; + .map_err(|e| format!("创建大纲节点失败: {e}"))?; tracing::info!( "[Memory] 创建大纲节点: id={}, title={}", @@ -522,46 +489,38 @@ impl MemoryManager { /// 获取大纲节点 pub fn get_outline_node(&self, id: &OutlineNodeId) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT id, project_id, parent_id, title, content, content_id, sort_order, expanded, extra_json, created_at, updated_at FROM outline_nodes WHERE id = ?", params![id], - |row| Ok(Self::row_to_outline_node(row)?), + Self::row_to_outline_node, ); match result { Ok(node) => Ok(Some(node)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取大纲节点失败: {}", e)), + Err(e) => Err(format!("获取大纲节点失败: {e}")), } } /// 列出项目的所有大纲节点 pub fn list_outline_nodes(&self, project_id: &str) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( "SELECT id, project_id, parent_id, title, content, content_id, sort_order, expanded, extra_json, created_at, updated_at FROM outline_nodes WHERE project_id = ? ORDER BY sort_order ASC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let nodes = stmt - .query_map(params![project_id], |row| { - Ok(Self::row_to_outline_node(row)?) - }) - .map_err(|e| format!("查询失败: {}", e))? + .query_map(params![project_id], |row| Self::row_to_outline_node(row)) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(nodes) } @@ -572,10 +531,7 @@ impl MemoryManager { id: &OutlineNodeId, updates: OutlineNodeUpdateRequest, ) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let now = Utc::now().timestamp_millis(); let mut set_clauses = vec!["updated_at = ?"]; @@ -620,7 +576,7 @@ impl MemoryManager { params_vec.iter().map(|p| p.as_ref()).collect(); conn.execute(&sql, params_refs.as_slice()) - .map_err(|e| format!("更新大纲节点失败: {}", e))?; + .map_err(|e| format!("更新大纲节点失败: {e}"))?; drop(conn); self.get_outline_node(id)? @@ -629,14 +585,11 @@ impl MemoryManager { /// 删除大纲节点 pub fn delete_outline_node(&self, id: &OutlineNodeId) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let affected = conn .execute("DELETE FROM outline_nodes WHERE id = ?", params![id]) - .map_err(|e| format!("删除大纲节点失败: {}", e))?; + .map_err(|e| format!("删除大纲节点失败: {e}"))?; if affected > 0 { tracing::info!("[Memory] 删除大纲节点: id={}", id); @@ -649,10 +602,7 @@ impl MemoryManager { project_id: &str, parent_id: Option<&str>, ) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result: Result = match parent_id { Some(pid) => conn.query_row( @@ -667,7 +617,7 @@ impl MemoryManager { ), }; - result.map_err(|e| format!("获取排序顺序失败: {}", e)) + result.map_err(|e| format!("获取排序顺序失败: {e}")) } fn row_to_outline_node(row: &rusqlite::Row) -> Result { diff --git a/src-tauri/src/memory/types.rs b/src-tauri/src/memory/types.rs index 50a5cd58d..cd2a481b9 100644 --- a/src-tauri/src/memory/types.rs +++ b/src-tauri/src/memory/types.rs @@ -309,7 +309,7 @@ pub struct OutlineNodeUpdateRequest { } /// 项目记忆(聚合所有记忆数据) -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ProjectMemory { /// 角色列表 pub characters: Vec, @@ -322,14 +322,3 @@ pub struct ProjectMemory { /// 大纲 pub outline: Vec, } - -impl Default for ProjectMemory { - fn default() -> Self { - Self { - characters: Vec::new(), - world_building: None, - style_guide: None, - outline: Vec::new(), - } - } -} diff --git a/src-tauri/src/middleware/tests.rs b/src-tauri/src/middleware/tests.rs index 9920c60cc..93ff65b0f 100644 --- a/src-tauri/src/middleware/tests.rs +++ b/src-tauri/src/middleware/tests.rs @@ -48,7 +48,7 @@ fn arb_ip_addr() -> impl Strategy { if a == 127 { None } else { - Some(format!("{}.{}.{}.{}", a, b, c, d)) + Some(format!("{a}.{b}.{c}.{d}")) } } ), @@ -146,7 +146,7 @@ fn test_management_auth_rate_limit_after_failures() { let octet3 = ((unique_id >> 8) & 0xFF) as u8; let octet4 = (unique_id & 0xFF) as u8; let client_ip = format!("198.51.{}.{}", 100 + (octet3 % 155), octet4.max(1)); - let addr: SocketAddr = format!("{}:12345", client_ip).parse().unwrap(); + let addr: SocketAddr = format!("{client_ip}:12345").parse().unwrap(); // 发送 5 次失败请求,每次都应该返回 401 for i in 0..5 { @@ -234,8 +234,8 @@ proptest! { let mut service = layer.layer(MockService); // Create request with WRONG auth header (append "wrong" to make it different) - let wrong_key = format!("{}wrong", secret_key); - let req = create_request_with_auth(Some(&format!("Bearer {}", wrong_key))); + let wrong_key = format!("{secret_key}wrong"); + let req = create_request_with_auth(Some(&format!("Bearer {wrong_key}"))); // Execute the service let rt = tokio::runtime::Runtime::new().unwrap(); @@ -272,7 +272,7 @@ proptest! { let mut service = layer.layer(MockService); // Create request with CORRECT auth header - let req = create_request_with_auth(Some(&format!("Bearer {}", secret_key))); + let req = create_request_with_auth(Some(&format!("Bearer {secret_key}"))); // Execute the service let rt = tokio::runtime::Runtime::new().unwrap(); @@ -346,7 +346,7 @@ proptest! { let mut service = layer.layer(MockService); // Create request with WRONG X-Management-Key header - let wrong_key = format!("{}wrong", secret_key); + let wrong_key = format!("{secret_key}wrong"); let req = create_request_with_management_key(Some(&wrong_key)); // Execute the service diff --git a/src-tauri/src/models/kiro_fingerprint.rs b/src-tauri/src/models/kiro_fingerprint.rs index afefc5bea..384eb743c 100644 --- a/src-tauri/src/models/kiro_fingerprint.rs +++ b/src-tauri/src/models/kiro_fingerprint.rs @@ -39,8 +39,7 @@ impl KiroFingerprintStore { // 确保目录存在 if !app_data_dir.exists() { - fs::create_dir_all(&app_data_dir) - .map_err(|e| format!("创建应用数据目录失败: {}", e))?; + fs::create_dir_all(&app_data_dir).map_err(|e| format!("创建应用数据目录失败: {e}"))?; } Ok(app_data_dir.join("kiro_fingerprints.json")) @@ -55,18 +54,18 @@ impl KiroFingerprintStore { } let content = - fs::read_to_string(&path).map_err(|e| format!("读取指纹存储文件失败: {}", e))?; + fs::read_to_string(&path).map_err(|e| format!("读取指纹存储文件失败: {e}"))?; - serde_json::from_str(&content).map_err(|e| format!("解析指纹存储文件失败: {}", e)) + serde_json::from_str(&content).map_err(|e| format!("解析指纹存储文件失败: {e}")) } /// 保存到文件 pub fn save(&self) -> Result<(), String> { let path = Self::get_storage_path()?; let content = - serde_json::to_string_pretty(self).map_err(|e| format!("序列化指纹存储失败: {}", e))?; + serde_json::to_string_pretty(self).map_err(|e| format!("序列化指纹存储失败: {e}"))?; - fs::write(&path, content).map_err(|e| format!("写入指纹存储文件失败: {}", e)) + fs::write(&path, content).map_err(|e| format!("写入指纹存储文件失败: {e}")) } /// 获取凭证的指纹绑定 @@ -141,7 +140,7 @@ fn generate_stable_machine_id( let result = hasher.finalize(); // 将哈希结果转换为 UUID 格式 - let hex = format!("{:x}", result); + let hex = format!("{result:x}"); format!( "{}-{}-{}-{}-{}", &hex[0..8], diff --git a/src-tauri/src/models/model_registry.rs b/src-tauri/src/models/model_registry.rs index db4d1ffb9..d21847b1c 100644 --- a/src-tauri/src/models/model_registry.rs +++ b/src-tauri/src/models/model_registry.rs @@ -109,7 +109,7 @@ impl std::str::FromStr for ModelStatus { "beta" => Ok(Self::Beta), "deprecated" => Ok(Self::Deprecated), "legacy" => Ok(Self::Legacy), - _ => Err(format!("Unknown model status: {}", s)), + _ => Err(format!("Unknown model status: {s}")), } } } @@ -150,7 +150,7 @@ impl std::str::FromStr for ModelTier { "mini" => Ok(Self::Mini), "pro" => Ok(Self::Pro), "max" => Ok(Self::Max), - _ => Err(format!("Unknown model tier: {}", s)), + _ => Err(format!("Unknown model tier: {s}")), } } } @@ -199,7 +199,7 @@ impl std::str::FromStr for ModelSource { "local" => Ok(Self::Local), "custom" => Ok(Self::Custom), "api" => Ok(Self::Api), - _ => Err(format!("Unknown model source: {}", s)), + _ => Err(format!("Unknown model source: {s}")), } } } @@ -370,7 +370,7 @@ impl UserModelPreference { } /// 模型同步状态 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ModelSyncState { /// 最后同步时间 (Unix 时间戳) pub last_sync_at: Option, @@ -382,17 +382,6 @@ pub struct ModelSyncState { pub last_error: Option, } -impl Default for ModelSyncState { - fn default() -> Self { - Self { - last_sync_at: None, - model_count: 0, - is_syncing: false, - last_error: None, - } - } -} - // ============================================================================ // Provider Alias 相关类型(用于 Kiro、Antigravity 等中转服务) // ============================================================================ diff --git a/src-tauri/src/models/route_model.rs b/src-tauri/src/models/route_model.rs index 528f7fb7d..5a8bfe24c 100644 --- a/src-tauri/src/models/route_model.rs +++ b/src-tauri/src/models/route_model.rs @@ -72,7 +72,7 @@ impl RouteInfo { "openai" => format!("/{}/v1/chat/completions", self.selector), _ => return, }; - let url = format!("{}{}", base_url, path); + let url = format!("{base_url}{path}"); self.endpoints.push(RouteEndpoint { path, protocol: protocol.to_string(), @@ -98,11 +98,10 @@ impl RouteInfo { model, format!( r#"{{ - "model": "{}", + "model": "{model}", "max_tokens": 1024, "messages": [{{"role": "user", "content": "Hello!"}}] -}}"#, - model +}}"# ), ) } @@ -118,10 +117,9 @@ impl RouteInfo { model, format!( r#"{{ - "model": "{}", + "model": "{model}", "messages": [{{"role": "user", "content": "Hello!"}}] -}}"#, - model +}}"# ), ) } diff --git a/src-tauri/src/orchestrator/fallback.rs b/src-tauri/src/orchestrator/fallback.rs index 429fd1d60..aef578b0c 100644 --- a/src-tauri/src/orchestrator/fallback.rs +++ b/src-tauri/src/orchestrator/fallback.rs @@ -8,10 +8,12 @@ use serde::{Deserialize, Serialize}; /// 降级策略 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] +#[derive(Default)] pub enum FallbackPolicy { /// 不降级,直接失败 None, /// 降级到下一个等级 + #[default] NextTier, /// 降级到任意可用模型 AnyAvailable, @@ -19,12 +21,6 @@ pub enum FallbackPolicy { Specific, } -impl Default for FallbackPolicy { - fn default() -> Self { - FallbackPolicy::NextTier - } -} - /// 降级结果 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FallbackResult { @@ -110,7 +106,7 @@ impl FallbackHandler { model: None, original_tier, fallback_tier: None, - reason: format!("降级策略为 None,不进行降级: {}", reason), + reason: format!("降级策略为 None,不进行降级: {reason}"), attempts: 0, }, @@ -131,7 +127,7 @@ impl FallbackHandler { model: Some(model), original_tier, fallback_tier: Some(tier), - reason: format!("从 {} 降级到 {}: {}", original_tier, tier, reason), + reason: format!("从 {original_tier} 降级到 {tier}: {reason}"), attempts, }; } @@ -143,7 +139,7 @@ impl FallbackHandler { model: None, original_tier, fallback_tier: None, - reason: format!("所有降级等级都没有可用模型: {}", reason), + reason: format!("所有降级等级都没有可用模型: {reason}"), attempts, } } @@ -165,7 +161,7 @@ impl FallbackHandler { model: Some(model), original_tier, fallback_tier: Some(tier), - reason: format!("选择任意可用模型 (等级 {}): {}", tier, reason), + reason: format!("选择任意可用模型 (等级 {tier}): {reason}"), attempts, }; } @@ -177,7 +173,7 @@ impl FallbackHandler { model: None, original_tier, fallback_tier: None, - reason: format!("没有任何可用模型: {}", reason), + reason: format!("没有任何可用模型: {reason}"), attempts, } } @@ -195,7 +191,7 @@ impl FallbackHandler { model: Some(model), original_tier, fallback_tier: Some(*tier), - reason: format!("使用指定备用模型 {}: {}", fallback_id, reason), + reason: format!("使用指定备用模型 {fallback_id}: {reason}"), attempts: 1, }; } @@ -206,7 +202,7 @@ impl FallbackHandler { model: None, original_tier, fallback_tier: None, - reason: format!("指定的备用模型 {} 不可用: {}", fallback_id, reason), + reason: format!("指定的备用模型 {fallback_id} 不可用: {reason}"), attempts: 1, } } else { @@ -215,7 +211,7 @@ impl FallbackHandler { model: None, original_tier, fallback_tier: None, - reason: format!("未配置备用模型: {}", reason), + reason: format!("未配置备用模型: {reason}"), attempts: 0, } } diff --git a/src-tauri/src/orchestrator/selector.rs b/src-tauri/src/orchestrator/selector.rs index 9606bc79a..07ace5fa7 100644 --- a/src-tauri/src/orchestrator/selector.rs +++ b/src-tauri/src/orchestrator/selector.rs @@ -93,7 +93,7 @@ impl ModelSelector { .tier_configs .get(&ctx.tier) .cloned() - .unwrap_or_else(|| TierConfig::pro()); + .unwrap_or_else(TierConfig::pro); // 获取策略 let registry = self.registry.read().await; @@ -173,7 +173,7 @@ impl ModelSelector { .tier_configs .get(&fallback_tier) .cloned() - .unwrap_or_else(|| TierConfig::pro()); + .unwrap_or_else(TierConfig::pro); let registry = self.registry.read().await; let strategy = registry diff --git a/src-tauri/src/orchestrator/strategies/cost_optimized.rs b/src-tauri/src/orchestrator/strategies/cost_optimized.rs index 34b80a8e1..cf13268eb 100644 --- a/src-tauri/src/orchestrator/strategies/cost_optimized.rs +++ b/src-tauri/src/orchestrator/strategies/cost_optimized.rs @@ -99,7 +99,7 @@ impl SelectionStrategy for CostOptimizedStrategy { Ok(ModelSelection { model: selected, - reason: format!("成本优先选择 (估算成本: {:.2})", cost), + reason: format!("成本优先选择 (估算成本: {cost:.2})"), confidence: 90, alternatives: available, }) diff --git a/src-tauri/src/orchestrator/strategies/load_balanced.rs b/src-tauri/src/orchestrator/strategies/load_balanced.rs index 738fceb2b..9ea1b4356 100644 --- a/src-tauri/src/orchestrator/strategies/load_balanced.rs +++ b/src-tauri/src/orchestrator/strategies/load_balanced.rs @@ -86,7 +86,7 @@ impl SelectionStrategy for LoadBalancedStrategy { Ok(ModelSelection { model: selected, - reason: format!("负载均衡选择 (当前负载: {}%)", load), + reason: format!("负载均衡选择 (当前负载: {load}%)"), confidence: 80, alternatives: available, }) diff --git a/src-tauri/src/orchestrator/strategies/round_robin.rs b/src-tauri/src/orchestrator/strategies/round_robin.rs index 88726e5e9..437f2f209 100644 --- a/src-tauri/src/orchestrator/strategies/round_robin.rs +++ b/src-tauri/src/orchestrator/strategies/round_robin.rs @@ -78,7 +78,7 @@ impl SelectionStrategy for RoundRobinStrategy { Ok(ModelSelection { model: selected, - reason: format!("轮询选择 (索引 {})", idx), + reason: format!("轮询选择 (索引 {idx})"), confidence: 80, alternatives, }) diff --git a/src-tauri/src/orchestrator/strategies/task_based.rs b/src-tauri/src/orchestrator/strategies/task_based.rs index 688764479..198a25cfd 100644 --- a/src-tauri/src/orchestrator/strategies/task_based.rs +++ b/src-tauri/src/orchestrator/strategies/task_based.rs @@ -170,7 +170,7 @@ impl SelectionStrategy for TaskBasedStrategy { Ok(ModelSelection { model: selected, - reason: format!("任务匹配选择 (任务类型: {})", task_name), + reason: format!("任务匹配选择 (任务类型: {task_name})"), confidence: 85, alternatives: available, }) diff --git a/src-tauri/src/orchestrator/tier.rs b/src-tauri/src/orchestrator/tier.rs index e1f43da9b..ec79882f0 100644 --- a/src-tauri/src/orchestrator/tier.rs +++ b/src-tauri/src/orchestrator/tier.rs @@ -8,10 +8,12 @@ use std::collections::HashMap; /// 服务等级 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum ServiceTier { /// Mini - 快速响应,适合简单任务 Mini, /// Pro - 均衡选择,适合大多数任务 + #[default] Pro, /// Max - 最强能力,适合复杂任务 Max, @@ -61,12 +63,6 @@ impl ServiceTier { } } -impl Default for ServiceTier { - fn default() -> Self { - ServiceTier::Pro - } -} - impl std::fmt::Display for ServiceTier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.display_name()) diff --git a/src-tauri/src/plugin/binary_downloader.rs b/src-tauri/src/plugin/binary_downloader.rs index a8ff14eac..54d3566ef 100644 --- a/src-tauri/src/plugin/binary_downloader.rs +++ b/src-tauri/src/plugin/binary_downloader.rs @@ -53,12 +53,12 @@ impl BinaryDownloader { /// 获取当前平台的二进制文件名 pub fn get_platform_binary_name(base_name: &str) -> String { match (std::env::consts::ARCH, std::env::consts::OS) { - ("aarch64", "macos") => format!("{}-aarch64-apple-darwin", base_name), - ("x86_64", "macos") => format!("{}-x86_64-apple-darwin", base_name), - ("x86_64", "linux") => format!("{}-x86_64-unknown-linux-gnu", base_name), - ("aarch64", "linux") => format!("{}-aarch64-unknown-linux-gnu", base_name), - ("x86_64", "windows") => format!("{}-x86_64-pc-windows-msvc.exe", base_name), - _ => format!("{}-unknown", base_name), + ("aarch64", "macos") => format!("{base_name}-aarch64-apple-darwin"), + ("x86_64", "macos") => format!("{base_name}-x86_64-apple-darwin"), + ("x86_64", "linux") => format!("{base_name}-x86_64-unknown-linux-gnu"), + ("aarch64", "linux") => format!("{base_name}-aarch64-unknown-linux-gnu"), + ("x86_64", "windows") => format!("{base_name}-x86_64-pc-windows-msvc.exe"), + _ => format!("{base_name}-unknown"), } } @@ -68,10 +68,8 @@ impl BinaryDownloader { github_owner: &str, github_repo: &str, ) -> Result<(String, Vec), String> { - let api_url = format!( - "https://api.github.com/repos/{}/{}/releases/latest", - github_owner, github_repo - ); + let api_url = + format!("https://api.github.com/repos/{github_owner}/{github_repo}/releases/latest"); info!("获取最新版本: {}", api_url); @@ -81,18 +79,18 @@ impl BinaryDownloader { .header("Accept", "application/vnd.github.v3+json") .send() .await - .map_err(|e| format!("请求 GitHub API 失败: {}", e))?; + .map_err(|e| format!("请求 GitHub API 失败: {e}"))?; if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("GitHub API 请求失败: {} - {}", status, body)); + return Err(format!("GitHub API 请求失败: {status} - {body}")); } let data: serde_json::Value = response .json() .await - .map_err(|e| format!("解析响应失败: {}", e))?; + .map_err(|e| format!("解析响应失败: {e}"))?; let version = data["tag_name"] .as_str() @@ -139,7 +137,7 @@ impl BinaryDownloader { .get(download_url) .send() .await - .map_err(|e| format!("下载请求失败: {}", e))?; + .map_err(|e| format!("下载请求失败: {e}"))?; if !response.status().is_success() { return Err(format!("下载失败: HTTP {}", response.status())); @@ -152,29 +150,29 @@ impl BinaryDownloader { if let Some(parent) = target_path.parent() { fs::create_dir_all(parent) .await - .map_err(|e| format!("创建目录失败: {}", e))?; + .map_err(|e| format!("创建目录失败: {e}"))?; } let mut file = fs::File::create(target_path) .await - .map_err(|e| format!("创建文件失败: {}", e))?; + .map_err(|e| format!("创建文件失败: {e}"))?; let mut downloaded: u64 = 0; let mut stream = response.bytes_stream(); use futures::StreamExt; while let Some(chunk) = stream.next().await { - let chunk = chunk.map_err(|e| format!("读取数据失败: {}", e))?; + let chunk = chunk.map_err(|e| format!("读取数据失败: {e}"))?; file.write_all(&chunk) .await - .map_err(|e| format!("写入文件失败: {}", e))?; + .map_err(|e| format!("写入文件失败: {e}"))?; downloaded += chunk.len() as u64; progress_callback(downloaded, total_size); } file.flush() .await - .map_err(|e| format!("刷新文件失败: {}", e))?; + .map_err(|e| format!("刷新文件失败: {e}"))?; // 设置可执行权限 (Unix) #[cfg(unix)] @@ -182,12 +180,12 @@ impl BinaryDownloader { use std::os::unix::fs::PermissionsExt; let metadata = fs::metadata(target_path) .await - .map_err(|e| format!("获取文件权限失败: {}", e))?; + .map_err(|e| format!("获取文件权限失败: {e}"))?; let mut perms = metadata.permissions(); perms.set_mode(0o755); fs::set_permissions(target_path, perms) .await - .map_err(|e| format!("设置可执行权限失败: {}", e))?; + .map_err(|e| format!("设置可执行权限失败: {e}"))?; } info!("下载完成: {:?}", target_path); @@ -202,7 +200,7 @@ impl BinaryDownloader { ) -> Result { let content = fs::read(file_path) .await - .map_err(|e| format!("读取文件失败: {}", e))?; + .map_err(|e| format!("读取文件失败: {e}"))?; let mut hasher = Sha256::new(); hasher.update(&content); @@ -225,14 +223,14 @@ impl BinaryDownloader { let checksum_asset = assets .iter() .find(|a| a.name == checksum_filename) - .ok_or_else(|| format!("未找到校验文件: {}", checksum_filename))?; + .ok_or_else(|| format!("未找到校验文件: {checksum_filename}"))?; let response = self .client .get(&checksum_asset.download_url) .send() .await - .map_err(|e| format!("下载校验文件失败: {}", e))?; + .map_err(|e| format!("下载校验文件失败: {e}"))?; if !response.status().is_success() { return Err(format!("下载校验文件失败: HTTP {}", response.status())); @@ -241,7 +239,7 @@ impl BinaryDownloader { let content = response .text() .await - .map_err(|e| format!("读取校验文件失败: {}", e))?; + .map_err(|e| format!("读取校验文件失败: {e}"))?; let mut checksums = HashMap::new(); for line in content.lines() { diff --git a/src-tauri/src/plugin/installer/downloader.rs b/src-tauri/src/plugin/installer/downloader.rs index 8525a49c3..93591f82a 100644 --- a/src-tauri/src/plugin/installer/downloader.rs +++ b/src-tauri/src/plugin/installer/downloader.rs @@ -59,7 +59,7 @@ impl PluginDownloader { // 创建目标文件 let mut file = tokio::fs::File::create(dest) .await - .map_err(|e| InstallError::IoError(e))?; + .map_err(InstallError::IoError)?; // 流式下载 use tokio::io::AsyncWriteExt; @@ -70,7 +70,7 @@ impl PluginDownloader { let chunk = chunk.map_err(|e| InstallError::NetworkError(e.to_string()))?; file.write_all(&chunk) .await - .map_err(|e| InstallError::IoError(e))?; + .map_err(InstallError::IoError)?; downloaded += chunk.len() as u64; @@ -95,7 +95,7 @@ impl PluginDownloader { progress.on_progress(InstallProgress::downloading(percent, message)); } - file.flush().await.map_err(|e| InstallError::IoError(e))?; + file.flush().await.map_err(InstallError::IoError)?; progress.on_progress(InstallProgress::downloading(100, "下载完成")); @@ -133,8 +133,7 @@ impl PluginDownloader { } Err(InstallError::UrlParseError(format!( - "无法解析 GitHub URL: {}", - url + "无法解析 GitHub URL: {url}" ))) } @@ -350,10 +349,7 @@ mod property_tests { "[a-z][a-z0-9_-]{0,49}\\.zip", // asset ) .prop_map(|(owner, repo, tag, asset)| { - format!( - "https://github.com/{}/{}/releases/download/{}/{}", - owner, repo, tag, asset - ) + format!("https://github.com/{owner}/{repo}/releases/download/{tag}/{asset}") }) } @@ -365,7 +361,7 @@ mod property_tests { "v[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}", // tag ) .prop_map(|(owner, repo, tag)| { - format!("https://github.com/{}/{}/releases/tag/{}", owner, repo, tag) + format!("https://github.com/{owner}/{repo}/releases/tag/{tag}") }) } @@ -377,7 +373,7 @@ mod property_tests { "v[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}", // tag ) .prop_map(|(owner, repo, _tag)| { - format!("{}/@{}", owner, repo) + format!("{owner}/@{repo}") .replace("/@", &format!("/{}@", repo.chars().next().unwrap_or('r'))) }) .prop_map(|_| "owner/repo@v1.0.0".to_string()) // 简化生成 @@ -389,7 +385,7 @@ mod property_tests { "[a-z][a-z0-9_-]{0,38}", // owner "[a-z][a-z0-9_-]{0,99}", // repo ) - .prop_map(|(owner, repo)| format!("{}/{}", owner, repo)) + .prop_map(|(owner, repo)| format!("{owner}/{repo}")) } proptest! { diff --git a/src-tauri/src/plugin/installer/installer.rs b/src-tauri/src/plugin/installer/installer.rs index 177841825..dbba6e77b 100644 --- a/src-tauri/src/plugin/installer/installer.rs +++ b/src-tauri/src/plugin/installer/installer.rs @@ -343,13 +343,13 @@ impl PluginInstaller { ) -> Result<(), InstallError> { let file = File::open(path)?; let mut archive = zip::ZipArchive::new(file) - .map_err(|e| InstallError::ExtractFailed(format!("无法读取 ZIP 文件: {}", e)))?; + .map_err(|e| InstallError::ExtractFailed(format!("无法读取 ZIP 文件: {e}")))?; let total = archive.len(); for i in 0..total { - let mut file = archive.by_index(i).map_err(|e| { - InstallError::ExtractFailed(format!("无法读取 ZIP 条目 {}: {}", i, e)) - })?; + let mut file = archive + .by_index(i) + .map_err(|e| InstallError::ExtractFailed(format!("无法读取 ZIP 条目 {i}: {e}")))?; let outpath = match file.enclosed_name() { Some(path) => dest.join(path), @@ -410,26 +410,26 @@ impl PluginInstaller { let mut archive_for_count = tar::Archive::new(gz_for_count); let total = archive_for_count .entries() - .map_err(|e| InstallError::ExtractFailed(format!("无法读取 tar.gz: {}", e)))? + .map_err(|e| InstallError::ExtractFailed(format!("无法读取 tar.gz: {e}")))? .count(); let mut count = 0; for entry in archive .entries() - .map_err(|e| InstallError::ExtractFailed(format!("无法读取 tar.gz: {}", e)))? + .map_err(|e| InstallError::ExtractFailed(format!("无法读取 tar.gz: {e}")))? { let mut entry = entry - .map_err(|e| InstallError::ExtractFailed(format!("tar.gz 条目读取失败: {}", e)))?; + .map_err(|e| InstallError::ExtractFailed(format!("tar.gz 条目读取失败: {e}")))?; entry .unpack_in(dest) - .map_err(|e| InstallError::ExtractFailed(format!("解压失败: {}", e)))?; + .map_err(|e| InstallError::ExtractFailed(format!("解压失败: {e}")))?; count += 1; let percent = (count as f64 / total as f64 * 100.0) as u8; progress.on_progress(InstallProgress::extracting( percent, - format!("解压中 ({}/{})", count, total), + format!("解压中 ({count}/{total})"), )); } @@ -568,18 +568,17 @@ mod tests { /// 创建有效的测试插件包 (ZIP) fn create_test_plugin_zip(dir: &Path, name: &str, version: &str) -> PathBuf { - let file_path = dir.join(format!("{}.zip", name)); + let file_path = dir.join(format!("{name}.zip")); let manifest_json = format!( r#"{{ - "name": "{}", - "version": "{}", + "name": "{name}", + "version": "{version}", "description": "Test plugin", "entry": "config.json", "plugin_type": "script", "hooks": [] - }}"#, - name, version + }}"# ); let config_json = r#"{"enabled": true}"#; @@ -608,7 +607,7 @@ mod tests { let progress = NoopProgressCallback; let result = installer.install_from_file(&package_path, &progress).await; - assert!(result.is_ok(), "安装应该成功: {:?}", result); + assert!(result.is_ok(), "安装应该成功: {result:?}"); let installed = result.unwrap(); assert_eq!(installed.name, "test-plugin"); @@ -647,7 +646,7 @@ mod tests { let result2 = installer .install_from_file(&package_path_v2, &progress) .await; - assert!(result2.is_ok(), "更新安装应该成功: {:?}", result2); + assert!(result2.is_ok(), "更新安装应该成功: {result2:?}"); let updated = result2.unwrap(); assert_eq!(updated.name, "update-plugin"); @@ -677,7 +676,7 @@ mod tests { assert!(result.is_err()); match result.unwrap_err() { InstallError::InvalidPackage(_) => {} - e => panic!("期望 InvalidPackage 错误,实际: {:?}", e), + e => panic!("期望 InvalidPackage 错误,实际: {e:?}"), } } @@ -701,7 +700,7 @@ mod tests { // 卸载 let result = installer.uninstall("uninstall-test").await; - assert!(result.is_ok(), "卸载应该成功: {:?}", result); + assert!(result.is_ok(), "卸载应该成功: {result:?}"); // 验证已卸载 assert!(!installer.is_installed("uninstall-test").unwrap()); @@ -718,7 +717,7 @@ mod tests { InstallError::NotFound(name) => { assert_eq!(name, "non-existent"); } - e => panic!("期望 NotFound 错误,实际: {:?}", e), + e => panic!("期望 NotFound 错误,实际: {e:?}"), } } @@ -787,7 +786,7 @@ mod property_tests { /// 生成有效的版本号 fn arb_valid_version() -> impl Strategy { (1u32..10, 0u32..10, 0u32..10) - .prop_map(|(major, minor, patch)| format!("{}.{}.{}", major, minor, patch)) + .prop_map(|(major, minor, patch)| format!("{major}.{minor}.{patch}")) } /// 创建测试用的安装器 @@ -809,18 +808,17 @@ mod property_tests { /// 创建有效的测试插件包 (ZIP) fn create_valid_plugin_zip(dir: &Path, name: &str, version: &str) -> PathBuf { - let file_path = dir.join(format!("{}.zip", name)); + let file_path = dir.join(format!("{name}.zip")); let manifest_json = format!( r#"{{ - "name": "{}", - "version": "{}", + "name": "{name}", + "version": "{version}", "description": "Test plugin", "entry": "config.json", "plugin_type": "script", "hooks": [] - }}"#, - name, version + }}"# ); let config_json = r#"{"enabled": true}"#; @@ -843,7 +841,7 @@ mod property_tests { /// 创建无效的插件包(缺少 plugin.json) fn create_invalid_plugin_zip_no_manifest(dir: &Path, name: &str) -> PathBuf { - let file_path = dir.join(format!("{}-invalid.zip", name)); + let file_path = dir.join(format!("{name}-invalid.zip")); let file = File::create(&file_path).unwrap(); let mut zip = zip::ZipWriter::new(file); diff --git a/src-tauri/src/plugin/installer/registry.rs b/src-tauri/src/plugin/installer/registry.rs index ad627a829..3e7fbde67 100644 --- a/src-tauri/src/plugin/installer/registry.rs +++ b/src-tauri/src/plugin/installer/registry.rs @@ -28,7 +28,7 @@ impl PluginRegistry { // 设置 busy_timeout 为 5 秒,避免 "database is locked" 错误 conn.busy_timeout(std::time::Duration::from_secs(5)) - .map_err(|e| InstallError::DatabaseError(format!("设置 busy_timeout 失败: {}", e)))?; + .map_err(|e| InstallError::DatabaseError(format!("设置 busy_timeout 失败: {e}")))?; Ok(Self { conn: Arc::new(Mutex::new(conn)), @@ -252,7 +252,7 @@ impl PluginRow { fn into_installed_plugin(self) -> Result { let source = deserialize_source(&self.source_type, self.source_data.as_deref())?; let installed_at = chrono::DateTime::parse_from_rfc3339(&self.installed_at) - .map_err(|e| InstallError::DatabaseError(format!("无效的时间格式: {}", e)))? + .map_err(|e| InstallError::DatabaseError(format!("无效的时间格式: {e}")))? .with_timezone(&chrono::Utc); Ok(InstalledPlugin { @@ -306,8 +306,7 @@ fn deserialize_source( }) } _ => Err(InstallError::DatabaseError(format!( - "未知的来源类型: {}", - source_type + "未知的来源类型: {source_type}" ))), } } @@ -329,11 +328,11 @@ mod tests { fn create_test_plugin(id: &str) -> InstalledPlugin { InstalledPlugin { id: id.to_string(), - name: format!("Test Plugin {}", id), + name: format!("Test Plugin {id}"), version: "1.0.0".to_string(), description: "A test plugin".to_string(), author: Some("Test Author".to_string()), - install_path: PathBuf::from(format!("/plugins/{}", id)), + install_path: PathBuf::from(format!("/plugins/{id}")), installed_at: chrono::Utc::now(), source: InstallSource::Local { path: "/tmp/plugin.zip".to_string(), diff --git a/src-tauri/src/plugin/installer/tests.rs b/src-tauri/src/plugin/installer/tests.rs index bd7e8c48b..8919d21ab 100644 --- a/src-tauri/src/plugin/installer/tests.rs +++ b/src-tauri/src/plugin/installer/tests.rs @@ -11,7 +11,7 @@ use zip::ZipWriter; /// 创建测试用的插件包 fn create_test_plugin_zip(dir: &TempDir, plugin_id: &str, version: &str) -> std::path::PathBuf { - let zip_path = dir.path().join(format!("{}.zip", plugin_id)); + let zip_path = dir.path().join(format!("{plugin_id}.zip")); let file = fs::File::create(&zip_path).unwrap(); let mut zip = ZipWriter::new(file); @@ -130,15 +130,15 @@ mod registry_tests { // 注册多个插件 for i in 1..=3 { let plugin = InstalledPlugin { - id: format!("plugin-{}", i), - name: format!("Plugin {}", i), + id: format!("plugin-{i}"), + name: format!("Plugin {i}"), version: "1.0.0".to_string(), - description: format!("Plugin {} description", i), + description: format!("Plugin {i} description"), author: Some("Author".to_string()), - install_path: temp_dir.path().join(format!("plugin-{}", i)), + install_path: temp_dir.path().join(format!("plugin-{i}")), installed_at: chrono::Utc::now(), source: InstallSource::Local { - path: format!("/tmp/plugin-{}.zip", i), + path: format!("/tmp/plugin-{i}.zip"), }, enabled: true, }; diff --git a/src-tauri/src/plugin/installer/validator.rs b/src-tauri/src/plugin/installer/validator.rs index 0bf2b1e6e..653a84337 100644 --- a/src-tauri/src/plugin/installer/validator.rs +++ b/src-tauri/src/plugin/installer/validator.rs @@ -69,7 +69,7 @@ impl PackageValidator { reader .read_exact(&mut magic) - .map_err(|e| InstallError::InvalidPackage(format!("无法读取文件头: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("无法读取文件头: {e}")))?; match format { PackageFormat::Zip => { @@ -111,24 +111,23 @@ impl PackageValidator { fn validate_zip_integrity(&self, path: &Path) -> Result<(), InstallError> { let file = File::open(path)?; let mut archive = zip::ZipArchive::new(file) - .map_err(|e| InstallError::InvalidPackage(format!("无法读取 ZIP 文件: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("无法读取 ZIP 文件: {e}")))?; // 检查是否为空压缩包 - if archive.len() == 0 { + if archive.is_empty() { return Err(InstallError::InvalidPackage("ZIP 压缩包为空".to_string())); } // 尝试读取每个文件的元数据以验证完整性 for i in 0..archive.len() { let file = archive.by_index(i).map_err(|e| { - InstallError::InvalidPackage(format!("ZIP 文件损坏,无法读取条目 {}: {}", i, e)) + InstallError::InvalidPackage(format!("ZIP 文件损坏,无法读取条目 {i}: {e}")) })?; // 验证文件名有效 if file.name().is_empty() { return Err(InstallError::InvalidPackage(format!( - "ZIP 条目 {} 的文件名无效", - i + "ZIP 条目 {i} 的文件名无效" ))); } } @@ -145,15 +144,15 @@ impl PackageValidator { let mut entry_count = 0; for entry in archive .entries() - .map_err(|e| InstallError::InvalidPackage(format!("无法读取 tar.gz 文件: {}", e)))? + .map_err(|e| InstallError::InvalidPackage(format!("无法读取 tar.gz 文件: {e}")))? { - let entry = entry - .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 文件损坏: {}", e)))?; + let entry = + entry.map_err(|e| InstallError::InvalidPackage(format!("tar.gz 文件损坏: {e}")))?; // 验证路径有效 let path = entry .path() - .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 条目路径无效: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 条目路径无效: {e}")))?; if path.to_string_lossy().is_empty() { return Err(InstallError::InvalidPackage( @@ -227,8 +226,7 @@ impl PackageValidator { for hook in &manifest.hooks { if !Self::is_valid_hook_name(hook) { return Err(InstallError::InvalidManifest(format!( - "无效的钩子名称: {}", - hook + "无效的钩子名称: {hook}" ))); } } @@ -323,7 +321,7 @@ impl PackageValidator { }; let manifest: PluginManifest = serde_json::from_str(&manifest_content) - .map_err(|e| InstallError::InvalidManifest(format!("plugin.json 解析失败: {}", e)))?; + .map_err(|e| InstallError::InvalidManifest(format!("plugin.json 解析失败: {e}")))?; self.validate_manifest(&manifest)?; @@ -334,19 +332,19 @@ impl PackageValidator { fn extract_manifest_from_zip(&self, path: &Path) -> Result { let file = File::open(path)?; let mut archive = zip::ZipArchive::new(file) - .map_err(|e| InstallError::InvalidPackage(format!("无法读取 ZIP 文件: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("无法读取 ZIP 文件: {e}")))?; // 查找 plugin.json(可能在根目录或子目录中) for i in 0..archive.len() { let mut file = archive .by_index(i) - .map_err(|e| InstallError::InvalidPackage(format!("无法读取 ZIP 条目: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("无法读取 ZIP 条目: {e}")))?; let name = file.name().to_string(); if name.ends_with("plugin.json") && !name.contains("__MACOSX") { let mut content = String::new(); file.read_to_string(&mut content).map_err(|e| { - InstallError::InvalidManifest(format!("无法读取 plugin.json: {}", e)) + InstallError::InvalidManifest(format!("无法读取 plugin.json: {e}")) })?; return Ok(content); } @@ -365,19 +363,19 @@ impl PackageValidator { for entry in archive .entries() - .map_err(|e| InstallError::InvalidPackage(format!("无法读取 tar.gz 文件: {}", e)))? + .map_err(|e| InstallError::InvalidPackage(format!("无法读取 tar.gz 文件: {e}")))? { let mut entry = entry - .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 条目读取失败: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 条目读取失败: {e}")))?; let entry_path = entry .path() - .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 条目路径无效: {}", e)))?; + .map_err(|e| InstallError::InvalidPackage(format!("tar.gz 条目路径无效: {e}")))?; if entry_path.ends_with("plugin.json") { let mut content = String::new(); entry.read_to_string(&mut content).map_err(|e| { - InstallError::InvalidManifest(format!("无法读取 plugin.json: {}", e)) + InstallError::InvalidManifest(format!("无法读取 plugin.json: {e}")) })?; return Ok(content); } @@ -472,8 +470,7 @@ mod tests { let manifest = create_test_manifest("test-plugin", version); assert!( validator.validate_manifest(&manifest).is_ok(), - "Version {} should be valid", - version + "Version {version} should be valid" ); } } @@ -626,7 +623,7 @@ mod tests { let validator = PackageValidator::new(); let result = validator.validate_format(&file_path); - assert!(result.is_ok(), "Expected Ok, got: {:?}", result); + assert!(result.is_ok(), "Expected Ok, got: {result:?}"); assert_eq!(result.unwrap(), PackageFormat::TarGz); } @@ -690,7 +687,7 @@ mod tests { let validator = PackageValidator::new(); let result = validator.extract_and_validate_manifest(&file_path, PackageFormat::TarGz); - assert!(result.is_ok(), "Expected Ok, got: {:?}", result); + assert!(result.is_ok(), "Expected Ok, got: {result:?}"); let manifest = result.unwrap(); assert_eq!(manifest.name, "test-plugin"); assert_eq!(manifest.version, "1.0.0"); @@ -777,7 +774,7 @@ mod property_tests { /// 生成有效的版本号 fn arb_valid_version() -> impl Strategy { (1u32..100, 0u32..100, 0u32..100) - .prop_map(|(major, minor, patch)| format!("{}.{}.{}", major, minor, patch)) + .prop_map(|(major, minor, patch)| format!("{major}.{minor}.{patch}")) } /// 生成有效的插件类型 @@ -802,20 +799,19 @@ mod property_tests { .prop_map(|(name, version, plugin_type, hooks, description)| { let hooks_json = hooks .iter() - .map(|h| format!("\"{}\"", h)) + .map(|h| format!("\"{h}\"")) .collect::>() .join(", "); let desc = description.unwrap_or_else(|| "Test plugin".to_string()); format!( r#"{{ - "name": "{}", - "version": "{}", - "description": "{}", + "name": "{name}", + "version": "{version}", + "description": "{desc}", "entry": "config.json", - "plugin_type": "{}", - "hooks": [{}] - }}"#, - name, version, desc, plugin_type, hooks_json + "plugin_type": "{plugin_type}", + "hooks": [{hooks_json}] + }}"# ) }) } diff --git a/src-tauri/src/plugin/loader.rs b/src-tauri/src/plugin/loader.rs index f93fb72ff..9b1aabe72 100644 --- a/src-tauri/src/plugin/loader.rs +++ b/src-tauri/src/plugin/loader.rs @@ -55,9 +55,9 @@ impl PluginLoader { let manifest_path = plugin_dir.join("manifest.json"); let content = fs::read_to_string(&manifest_path) .await - .map_err(|e| PluginError::LoadError(format!("无法读取清单文件: {}", e)))?; + .map_err(|e| PluginError::LoadError(format!("无法读取清单文件: {e}")))?; let manifest: PluginManifest = serde_json::from_str(&content) - .map_err(|e| PluginError::InvalidManifest(format!("解析失败: {}", e)))?; + .map_err(|e| PluginError::InvalidManifest(format!("解析失败: {e}")))?; manifest.validate()?; Ok(manifest) } @@ -87,7 +87,7 @@ impl PluginLoader { let plugin_settings = if config_path.exists() { let content = fs::read_to_string(&config_path) .await - .map_err(|e| PluginError::LoadError(format!("无法读取配置文件: {}", e)))?; + .map_err(|e| PluginError::LoadError(format!("无法读取配置文件: {e}")))?; serde_json::from_str(&content).unwrap_or_default() } else { serde_json::Value::Object(serde_json::Map::new()) diff --git a/src-tauri/src/plugin/manager.rs b/src-tauri/src/plugin/manager.rs index f529f3679..7afe1f7c1 100644 --- a/src-tauri/src/plugin/manager.rs +++ b/src-tauri/src/plugin/manager.rs @@ -129,7 +129,7 @@ impl PluginManager { // 检查是否已加载 if self.plugins.contains_key(&name) { - return Err(PluginError::LoadError(format!("插件 {} 已加载", name))); + return Err(PluginError::LoadError(format!("插件 {name} 已加载"))); } let mut instance = @@ -284,7 +284,7 @@ impl PluginManager { } Err(_) => { tracing::warn!("插件 {} on_request 执行超时", plugin_name); - HookResult::failure(format!("执行超时 ({}ms)", timeout_ms), timeout_ms) + HookResult::failure(format!("执行超时 ({timeout_ms}ms)"), timeout_ms) } }; @@ -338,7 +338,7 @@ impl PluginManager { } Err(_) => { tracing::warn!("插件 {} on_response 执行超时", plugin_name); - HookResult::failure(format!("执行超时 ({}ms)", timeout_ms), timeout_ms) + HookResult::failure(format!("执行超时 ({timeout_ms}ms)"), timeout_ms) } }; @@ -388,7 +388,7 @@ impl PluginManager { } Err(_) => { tracing::warn!("插件 {} on_error 执行超时", plugin_name); - HookResult::failure(format!("执行超时 ({}ms)", timeout_ms), timeout_ms) + HookResult::failure(format!("执行超时 ({timeout_ms}ms)"), timeout_ms) } }; diff --git a/src-tauri/src/providers/antigravity.rs b/src-tauri/src/providers/antigravity.rs index 5c36a2a0b..33d572b2a 100644 --- a/src-tauri/src/providers/antigravity.rs +++ b/src-tauri/src/providers/antigravity.rs @@ -187,13 +187,13 @@ impl TokenRefreshError { "Antigravity 授权已过期,请重新登录授权".to_string() } TokenRefreshError::NetworkError { message } => { - format!("网络连接失败: {}", message) + format!("网络连接失败: {message}") } TokenRefreshError::ServerError { message } => { - format!("Google 服务暂时不可用: {}", message) + format!("Google 服务暂时不可用: {message}") } TokenRefreshError::Unknown { message } => { - format!("Token 刷新失败: {}", message) + format!("Token 刷新失败: {message}") } } } @@ -285,7 +285,7 @@ fn generate_session_id() -> String { let n: u64 = u64::from_le_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], ]) % 9_000_000_000_000_000_000; - format!("-{}", n) + format!("-{n}") } /// 生成随机项目 ID @@ -297,7 +297,7 @@ fn generate_project_id() -> String { let adj = adjectives[(bytes[0] as usize) % adjectives.len()]; let noun = nouns[(bytes[1] as usize) % nouns.len()]; let random_part: String = uuid.to_string()[..5].to_lowercase(); - format!("{}-{}-{}", adj, noun, random_part) + format!("{adj}-{noun}-{random_part}") } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -624,14 +624,14 @@ impl AntigravityProvider { // 服务器错误 (5xx) if status >= 500 { return TokenRefreshError::ServerError { - message: format!("HTTP {}: {}", status, body), + message: format!("HTTP {status}: {body}"), }; } // 其他客户端错误 if status >= 400 { return TokenRefreshError::Unknown { - message: format!("HTTP {}: {}", status, body), + message: format!("HTTP {status}: {body}"), }; } @@ -728,7 +728,7 @@ impl AntigravityProvider { } Err(e) => { last_error = Some(TokenRefreshError::Unknown { - message: format!("解析响应失败: {}", e), + message: format!("解析响应失败: {e}"), }); } } @@ -850,12 +850,12 @@ impl AntigravityProvider { .as_ref() .ok_or_else(|| AntigravityApiError::new(401, "No access token"))?; - let url = format!("{}/{ANTIGRAVITY_API_VERSION}:{method}", base_url); + let url = format!("{base_url}/{ANTIGRAVITY_API_VERSION}:{method}"); // 打印详细的请求信息 eprintln!("========== [ANTIGRAVITY_API] 请求详情 =========="); - eprintln!("[ANTIGRAVITY_API] URL: {}", url); - eprintln!("[ANTIGRAVITY_API] Method: {}", method); + eprintln!("[ANTIGRAVITY_API] URL: {url}"); + eprintln!("[ANTIGRAVITY_API] Method: {method}"); eprintln!( "[ANTIGRAVITY_API] Token (前20字符): {}...", &token[..token.len().min(20)] @@ -875,33 +875,33 @@ impl AntigravityProvider { .send() .await .map_err(|e| { - eprintln!("[ANTIGRAVITY_API] 网络错误: {}", e); - AntigravityApiError::new(503, format!("Network error: {}", e)) + eprintln!("[ANTIGRAVITY_API] 网络错误: {e}"); + AntigravityApiError::new(503, format!("Network error: {e}")) })?; let status = resp.status(); let status_code = status.as_u16(); - eprintln!("[ANTIGRAVITY_API] 响应状态码: {}", status); + eprintln!("[ANTIGRAVITY_API] 响应状态码: {status}"); if !status.is_success() { let body_text = resp.text().await.unwrap_or_default(); - eprintln!("[ANTIGRAVITY_API] 错误响应体: {}", body_text); + eprintln!("[ANTIGRAVITY_API] 错误响应体: {body_text}"); eprintln!("========== [ANTIGRAVITY_API] 请求失败 =========="); return Err(AntigravityApiError::with_body( status_code, - format!("API call failed: {}", status), + format!("API call failed: {status}"), body_text, )); } - let response_text = resp.text().await.map_err(|e| { - AntigravityApiError::new(500, format!("Failed to read response: {}", e)) - })?; - eprintln!("[ANTIGRAVITY_API] 响应体: {}", response_text); + let response_text = resp + .text() + .await + .map_err(|e| AntigravityApiError::new(500, format!("Failed to read response: {e}")))?; + eprintln!("[ANTIGRAVITY_API] 响应体: {response_text}"); - let data: serde_json::Value = serde_json::from_str(&response_text).map_err(|e| { - AntigravityApiError::new(500, format!("Failed to parse response: {}", e)) - })?; + let data: serde_json::Value = serde_json::from_str(&response_text) + .map_err(|e| AntigravityApiError::new(500, format!("Failed to parse response: {e}")))?; eprintln!("========== [ANTIGRAVITY_API] 请求成功 =========="); Ok(data) @@ -1058,17 +1058,17 @@ impl AntigravityProvider { request_body: &serde_json::Value, ) -> Result { eprintln!("========== [ANTIGRAVITY_GENERATE] 开始生成内容 =========="); - eprintln!("[ANTIGRAVITY_GENERATE] 模型: {}", model); + eprintln!("[ANTIGRAVITY_GENERATE] 模型: {model}"); eprintln!( "[ANTIGRAVITY_GENERATE] 请求体: {}", serde_json::to_string_pretty(request_body).unwrap_or_default() ); let project_id = self.project_id.clone().unwrap_or_else(generate_project_id); - eprintln!("[ANTIGRAVITY_GENERATE] 项目ID: {}", project_id); + eprintln!("[ANTIGRAVITY_GENERATE] 项目ID: {project_id}"); let actual_model = alias_to_model_name(model); - eprintln!("[ANTIGRAVITY_GENERATE] 实际模型名: {}", actual_model); + eprintln!("[ANTIGRAVITY_GENERATE] 实际模型名: {actual_model}"); let payload = self.build_antigravity_request(&actual_model, &project_id, request_body); eprintln!( @@ -1176,7 +1176,7 @@ pub struct AntigravityOAuthResult { /// 生成 OAuth 授权 URL pub fn generate_auth_url(port: u16, state: &str) -> String { let scopes = OAUTH_SCOPES.join(" "); - let redirect_uri = format!("http://localhost:{}/oauth-callback", port); + let redirect_uri = format!("http://localhost:{port}/oauth-callback"); let params = [ ("access_type", "offline"), @@ -1194,7 +1194,7 @@ pub fn generate_auth_url(port: u16, state: &str) -> String { .collect::>() .join("&"); - format!("https://accounts.google.com/o/oauth2/v2/auth?{}", query) + format!("https://accounts.google.com/o/oauth2/v2/auth?{query}") } /// 用授权码交换 Token @@ -1220,7 +1220,7 @@ pub async fn exchange_code_for_token( if !resp.status().is_success() { let status = resp.status(); let body = resp.text().await.unwrap_or_default(); - return Err(format!("Token 交换失败: {} - {}", status, body).into()); + return Err(format!("Token 交换失败: {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; @@ -1234,7 +1234,7 @@ pub async fn fetch_user_email( ) -> Result, Box> { let resp = client .get("https://www.googleapis.com/oauth2/v2/userinfo") - .header("Authorization", format!("Bearer {}", access_token)) + .header("Authorization", format!("Bearer {access_token}")) .send() .await?; @@ -1268,7 +1268,7 @@ pub async fn fetch_project_id_for_oauth( let resp = client .post("https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:loadCodeAssist") - .header("Authorization", format!("Bearer {}", access_token)) + .header("Authorization", format!("Bearer {access_token}")) .header("User-Agent", "antigravity/1.11.9 windows/amd64") .header("Content-Type", "application/json") .json(&serde_json::json!({ "metadata": { "ideType": "ANTIGRAVITY" } })) @@ -1320,7 +1320,7 @@ pub async fn fetch_project_id_for_oauth( status, body ); - Err(format!("loadCodeAssist 请求失败: {} - {}", status, body).into()) + Err(format!("loadCodeAssist 请求失败: {status} - {body}").into()) } } @@ -1410,7 +1410,7 @@ pub async fn start_oauth_server_and_get_url( let listener = TcpListener::bind("127.0.0.1:0").await?; let port = listener.local_addr()?.port(); - let redirect_uri = format!("http://localhost:{}/oauth-callback", port); + let redirect_uri = format!("http://localhost:{port}/oauth-callback"); let redirect_uri_clone = redirect_uri.clone(); // 生成授权 URL @@ -1440,7 +1440,7 @@ pub async fn start_oauth_server_and_get_url( if let Some(err) = error { let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", err); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("OAuth 错误: {}", err))); + let _ = sender.send(Err(format!("OAuth 错误: {err}"))); } return Html(html); } @@ -1522,7 +1522,7 @@ pub async fn start_oauth_server_and_get_url( expires_in, timestamp: Some(now.timestamp_millis()), enable: Some(true), - project_id: project_id, + project_id, email: email.clone(), }; @@ -1534,7 +1534,7 @@ pub async fn start_oauth_server_and_get_url( .join("antigravity"); if let Err(e) = std::fs::create_dir_all(&creds_dir) { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1548,7 +1548,7 @@ pub async fn start_oauth_server_and_get_url( let creds_json = match serde_json::to_string_pretty(&credentials) { Ok(json) => json, Err(e) => { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("序列化失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("序列化失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1557,7 +1557,7 @@ pub async fn start_oauth_server_and_get_url( }; if let Err(e) = std::fs::write(&creds_path, &creds_json) { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1607,7 +1607,7 @@ pub async fn start_oauth_server_and_get_url( server_result = server => { match server_result { Ok(_) => Err("服务器意外关闭".into()), - Err(e) => Err(format!("服务器错误: {}", e).into()), + Err(e) => Err(format!("服务器错误: {e}").into()), } } } @@ -1638,9 +1638,9 @@ pub async fn start_oauth_login_with_port( let tx = Arc::new(tokio::sync::Mutex::new(Some(tx))); // 绑定到指定端口 - let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).await?; + let listener = TcpListener::bind(format!("127.0.0.1:{port}")).await?; - let redirect_uri = format!("http://localhost:{}/oauth-callback", port); + let redirect_uri = format!("http://localhost:{port}/oauth-callback"); let redirect_uri_clone = redirect_uri.clone(); // 构建路由 @@ -1661,7 +1661,7 @@ pub async fn start_oauth_login_with_port( if let Some(err) = error { let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", err); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("OAuth 错误: {}", err))); + let _ = sender.send(Err(format!("OAuth 错误: {err}"))); } return Html(html); } @@ -1743,7 +1743,7 @@ pub async fn start_oauth_login_with_port( expires_in, timestamp: Some(now.timestamp_millis()), enable: Some(true), - project_id: project_id, + project_id, email: email.clone(), }; @@ -1755,7 +1755,7 @@ pub async fn start_oauth_login_with_port( .join("antigravity"); if let Err(e) = std::fs::create_dir_all(&creds_dir) { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1769,7 +1769,7 @@ pub async fn start_oauth_login_with_port( let creds_json = match serde_json::to_string_pretty(&credentials) { Ok(json) => json, Err(e) => { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("序列化失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("序列化失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1778,7 +1778,7 @@ pub async fn start_oauth_login_with_port( }; if let Err(e) = std::fs::write(&creds_path, &creds_json) { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1826,7 +1826,7 @@ pub async fn start_oauth_login_with_port( server_result = server => { match server_result { Ok(_) => Err("服务器意外关闭".into()), - Err(e) => Err(format!("服务器错误: {}", e).into()), + Err(e) => Err(format!("服务器错误: {e}").into()), } } } @@ -1857,7 +1857,7 @@ pub async fn start_oauth_login( let listener = TcpListener::bind("127.0.0.1:0").await?; let port = listener.local_addr()?.port(); - let redirect_uri = format!("http://localhost:{}/oauth-callback", port); + let redirect_uri = format!("http://localhost:{port}/oauth-callback"); let redirect_uri_clone = redirect_uri.clone(); // 构建路由 @@ -1878,7 +1878,7 @@ pub async fn start_oauth_login( if let Some(err) = error { let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", err); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("OAuth 错误: {}", err))); + let _ = sender.send(Err(format!("OAuth 错误: {err}"))); } return Html(html); } @@ -1969,7 +1969,7 @@ pub async fn start_oauth_login( expires_in, timestamp: Some(now.timestamp_millis()), enable: Some(true), - project_id: project_id, + project_id, email: email.clone(), }; @@ -1981,7 +1981,7 @@ pub async fn start_oauth_login( .join("antigravity"); if let Err(e) = std::fs::create_dir_all(&creds_dir) { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -1995,7 +1995,7 @@ pub async fn start_oauth_login( let creds_json = match serde_json::to_string_pretty(&credentials) { Ok(json) => json, Err(e) => { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("序列化失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("序列化失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -2004,7 +2004,7 @@ pub async fn start_oauth_login( }; if let Err(e) = std::fs::write(&creds_path, &creds_json) { - let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {}", e)); + let html = OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {e}")); if let Some(sender) = tx.lock().await.take() { let _ = sender.send(Err(e.to_string())); } @@ -2063,7 +2063,7 @@ pub async fn start_oauth_login( server_result = server => { match server_result { Ok(_) => Err("服务器意外关闭".into()), - Err(e) => Err(format!("服务器错误: {}", e).into()), + Err(e) => Err(format!("服务器错误: {e}").into()), } } } @@ -2160,14 +2160,11 @@ impl StreamingProvider for AntigravityProvider { let mut last_error: Option = None; for base_url in &self.base_urls { - let url = format!( - "{}/{ANTIGRAVITY_API_VERSION}:streamGenerateContent", - base_url - ); + let url = format!("{base_url}/{ANTIGRAVITY_API_VERSION}:streamGenerateContent"); eprintln!("[ANTIGRAVITY_STREAM] ========== 发起 HTTP 请求 =========="); - eprintln!("[ANTIGRAVITY_STREAM] URL: {}", url); - eprintln!("[ANTIGRAVITY_STREAM] Model: {}", actual_model); + eprintln!("[ANTIGRAVITY_STREAM] URL: {url}"); + eprintln!("[ANTIGRAVITY_STREAM] Model: {actual_model}"); eprintln!( "[ANTIGRAVITY_STREAM] Token 前20字符: {}...", &token[..20.min(token.len())] @@ -2181,7 +2178,7 @@ impl StreamingProvider for AntigravityProvider { let result = self .client .post(&url) - .header("Authorization", format!("Bearer {}", token)) + .header("Authorization", format!("Bearer {token}")) .header("Content-Type", "application/json") .header("Accept", "text/event-stream") .header("User-Agent", "antigravity/1.11.9 windows/amd64") @@ -2192,7 +2189,7 @@ impl StreamingProvider for AntigravityProvider { match result { Ok(resp) => { let status = resp.status(); - eprintln!("[ANTIGRAVITY_STREAM] HTTP 响应状态: {}", status); + eprintln!("[ANTIGRAVITY_STREAM] HTTP 响应状态: {status}"); tracing::info!("[ANTIGRAVITY_STREAM] HTTP 响应状态: {}", status); if status.is_success() { @@ -2218,8 +2215,7 @@ impl StreamingProvider for AntigravityProvider { } Err(e) => { eprintln!( - "[ANTIGRAVITY_STREAM] ✗ 连接失败\n Base URL: {}\n Error: {}", - base_url, e + "[ANTIGRAVITY_STREAM] ✗ 连接失败\n Base URL: {base_url}\n Error: {e}" ); tracing::error!( "[ANTIGRAVITY_STREAM] ✗ 连接失败\n Base URL: {}\n Error: {}", @@ -2544,7 +2540,7 @@ mod tests { ]; for error in errors { - let display = format!("{}", error); + let display = format!("{error}"); assert!(!display.is_empty()); } } diff --git a/src-tauri/src/providers/claude_custom.rs b/src-tauri/src/providers/claude_custom.rs index a8857583f..64af8bbbe 100644 --- a/src-tauri/src/providers/claude_custom.rs +++ b/src-tauri/src/providers/claude_custom.rs @@ -82,9 +82,9 @@ impl ClaudeCustomProvider { // 如果用户输入了带 /v1 的 URL,直接拼接 endpoint // 否则拼接 /v1/endpoint if base.ends_with("/v1") { - format!("{}/{}", base, endpoint) + format!("{base}/{endpoint}") } else { - format!("{}/v1/{}", base, endpoint) + format!("{base}/v1/{endpoint}") } } @@ -596,11 +596,9 @@ impl StreamingProvider for ClaudeCustomProvider { serde_json::Value::Object(obj) => { // 处理 {"type": "function", "function": {"name": "xxx"}} 格式 if let Some(func) = obj.get("function") { - if let Some(name) = func.get("name").and_then(|n| n.as_str()) { - Some(serde_json::json!({"type": "tool", "name": name})) - } else { - None - } + func.get("name") + .and_then(|n| n.as_str()) + .map(|name| serde_json::json!({"type": "tool", "name": name})) } else if let Some(t) = obj.get("type").and_then(|t| t.as_str()) { match t { "any" | "tool" => Some(serde_json::json!({"type": "any"})), diff --git a/src-tauri/src/providers/claude_oauth.rs b/src-tauri/src/providers/claude_oauth.rs index e235339e2..e612bc7e1 100644 --- a/src-tauri/src/providers/claude_oauth.rs +++ b/src-tauri/src/providers/claude_oauth.rs @@ -405,7 +405,7 @@ pub fn generate_claude_auth_url(state: &str, code_challenge: &str) -> String { .collect::>() .join("&"); - format!("{}?{}", CLAUDE_AUTH_URL, query) + format!("{CLAUDE_AUTH_URL}?{query}") } /// 生成 Setup Token 授权 URL(只需要推理权限) @@ -427,7 +427,7 @@ pub fn generate_claude_setup_token_auth_url(state: &str, code_challenge: &str) - .collect::>() .join("&"); - format!("{}?{}", CLAUDE_AUTH_URL, query) + format!("{CLAUDE_AUTH_URL}?{query}") } /// 用授权码交换 Token(使用官方 redirect_uri) @@ -468,7 +468,7 @@ pub async fn exchange_claude_code_for_token( let status = resp.status(); let body = resp.text().await.unwrap_or_default(); tracing::error!("[CLAUDE_OAUTH] Token 交换失败: {} - {}", status, body); - return Err(format!("Token 交换失败: {} - {}", status, body).into()); + return Err(format!("Token 交换失败: {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; @@ -689,10 +689,10 @@ fn build_cookie_headers(session_key: &str) -> reqwest::header::HeaderMap { headers.insert("Cache-Control", "no-cache".parse().unwrap()); headers.insert( "Cookie", - format!("sessionKey={}", session_key).parse().unwrap(), + format!("sessionKey={session_key}").parse().unwrap(), ); headers.insert("Origin", CLAUDE_AI_URL.parse().unwrap()); - headers.insert("Referer", format!("{}/new", CLAUDE_AI_URL).parse().unwrap()); + headers.insert("Referer", format!("{CLAUDE_AI_URL}/new").parse().unwrap()); headers.insert( "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" @@ -725,7 +725,7 @@ async fn get_organization_info( if status.as_u16() == 302 { return Err("请求被 Cloudflare 拦截,请稍后重试".into()); } - return Err(format!("获取组织信息失败:HTTP {}", status).into()); + return Err(format!("获取组织信息失败:HTTP {status}").into()); } let data: serde_json::Value = resp.json().await?; @@ -782,7 +782,7 @@ async fn authorize_with_cookie( let state = Uuid::new_v4().to_string(); // 构建授权 URL - let authorize_url = format!("https://claude.ai/v1/oauth/{}/authorize", organization_uuid); + let authorize_url = format!("https://claude.ai/v1/oauth/{organization_uuid}/authorize"); // 构建请求 payload let payload = serde_json::json!({ @@ -817,7 +817,7 @@ async fn authorize_with_cookie( return Err("请求被 Cloudflare 拦截,请稍后重试".into()); } let body = resp.text().await.unwrap_or_default(); - return Err(format!("授权请求失败:HTTP {} - {}", status, body).into()); + return Err(format!("授权请求失败:HTTP {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; diff --git a/src-tauri/src/providers/codex.rs b/src-tauri/src/providers/codex.rs index e64fd4750..3ddb15ea5 100644 --- a/src-tauri/src/providers/codex.rs +++ b/src-tauri/src/providers/codex.rs @@ -260,7 +260,7 @@ impl OAuthServer { match result { Ok(Ok(callback_result)) => { if let Some(ref error) = callback_result.error { - Err(format!("OAuth error: {}", error).into()) + Err(format!("OAuth error: {error}").into()) } else { Ok(callback_result) } @@ -465,19 +465,19 @@ impl CodexProvider { // - 如果 base_url 只有域名(path 为空或 /):拼 /v1/responses(OpenAI 标准) // - 如果 base_url 已包含路径前缀(如 https://yunyi.cfd/codex):认为前缀已包含路由信息,拼 /responses if base.ends_with("/v1") { - return format!("{}/responses", base); + return format!("{base}/responses"); } if let Ok(parsed) = url::Url::parse(base) { let path = parsed.path().trim_end_matches('/'); if path.is_empty() || path == "/" { - return format!("{}/v1/responses", base); + return format!("{base}/v1/responses"); } - return format!("{}/responses", base); + return format!("{base}/responses"); } // 兜底:保持旧行为 - format!("{}/v1/responses", base) + format!("{base}/v1/responses") } /// Load credentials from the default path @@ -505,7 +505,7 @@ impl CodexProvider { // 尝试解析凭证文件 let creds: CodexCredentials = serde_json::from_str(&content).map_err(|e| { tracing::error!("[CODEX] 凭证文件解析失败: {}. 文件路径: {:?}", e, path); - format!("凭证文件格式错误: {}", e) + format!("凭证文件格式错误: {e}") })?; // 检查关键字段 @@ -539,7 +539,7 @@ impl CodexProvider { self.creds_path = Some(path.clone()); } else { tracing::warn!("[CODEX] 凭证文件不存在: {:?}", path); - return Err(format!("凭证文件不存在: {:?}", path).into()); + return Err(format!("凭证文件不存在: {path:?}").into()); } Ok(()) } @@ -609,7 +609,7 @@ impl CodexProvider { ]; let query = serde_urlencoded::to_string(params)?; - Ok(format!("{}?{}", OPENAI_AUTH_URL, query)) + Ok(format!("{OPENAI_AUTH_URL}?{query}")) } /// Generate a random state string for CSRF protection @@ -648,7 +648,7 @@ impl CodexProvider { if !resp.status().is_success() { let status = resp.status(); let body = resp.text().await.unwrap_or_default(); - return Err(format!("Token exchange failed: {} - {}", status, body).into()); + return Err(format!("Token exchange failed: {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; @@ -972,10 +972,7 @@ impl CodexProvider { "[CODEX] Failed to open browser: {}. Please open the URL manually.", e ); - println!( - "Please open the following URL in your browser:\n{}", - auth_url - ); + println!("Please open the following URL in your browser:\n{auth_url}"); } // Wait for callback (5 minute timeout) @@ -1094,7 +1091,7 @@ impl CodexProvider { Self::build_responses_url(base_url) } - AuthMode::OAuth => format!("{}/responses", CODEX_API_BASE_URL), + AuthMode::OAuth => format!("{CODEX_API_BASE_URL}/responses"), }; // Transform OpenAI chat completion request to Codex format @@ -1103,7 +1100,7 @@ impl CodexProvider { let mut req = self .client .post(&url) - .header("Authorization", format!("Bearer {}", token)) + .header("Authorization", format!("Bearer {token}")) .header("Content-Type", "application/json") .header("Accept", "text/event-stream") .header("Connection", "Keep-Alive") @@ -1800,7 +1797,7 @@ mod tests { let header = URL_SAFE_NO_PAD.encode(r#"{"alg":"RS256","typ":"JWT"}"#); let payload = URL_SAFE_NO_PAD.encode(r#"{"email":"test@example.com","sub":"user123","https://api.openai.com/auth":{"chatgpt_account_id":"chatgpt_acc_123","user_id":"uid_456"}}"#); - let mock_jwt = format!("{}.{}.signature", header, payload); + let mock_jwt = format!("{header}.{payload}.signature"); let (account_id, email) = parse_jwt_claims(&mock_jwt); assert_eq!(email, Some("test@example.com".to_string())); @@ -1816,7 +1813,7 @@ mod tests { let header = URL_SAFE_NO_PAD.encode(r#"{"alg":"RS256","typ":"JWT"}"#); let payload = URL_SAFE_NO_PAD.encode(r#"{"email":"test@example.com","sub":"user123","https://api.openai.com/auth":{"user_id":"uid_456"}}"#); - let mock_jwt = format!("{}.{}.signature", header, payload); + let mock_jwt = format!("{header}.{payload}.signature"); let (account_id, email) = parse_jwt_claims(&mock_jwt); assert_eq!(email, Some("test@example.com".to_string())); @@ -2057,10 +2054,8 @@ const OPENAI_OAUTH_CALLBACK_PATH: &str = "/auth/callback"; /// 注意:OpenAI OAuth 要求 redirect_uri 必须是预先注册的固定地址 /// Codex CLI 的 client_id 只注册了 http://localhost:1455/auth/callback pub fn generate_codex_auth_url(state: &str, code_challenge: &str) -> String { - let redirect_uri = format!( - "http://localhost:{}{}", - OPENAI_OAUTH_CALLBACK_PORT, OPENAI_OAUTH_CALLBACK_PATH - ); + let redirect_uri = + format!("http://localhost:{OPENAI_OAUTH_CALLBACK_PORT}{OPENAI_OAUTH_CALLBACK_PATH}"); let params = [ ("client_id", OPENAI_CLIENT_ID), @@ -2082,7 +2077,7 @@ pub fn generate_codex_auth_url(state: &str, code_challenge: &str) -> String { .collect::>() .join("&"); - format!("{}?{}", OPENAI_AUTH_URL, query) + format!("{OPENAI_AUTH_URL}?{query}") } /// 用授权码交换 Token @@ -2111,7 +2106,7 @@ pub async fn exchange_codex_code_for_token( if !resp.status().is_success() { let status = resp.status(); let body = resp.text().await.unwrap_or_default(); - return Err(format!("Token 交换失败: {} - {}", status, body).into()); + return Err(format!("Token 交换失败: {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; @@ -2211,21 +2206,18 @@ pub async fn start_codex_oauth_server_and_get_url() -> Result< // 使用固定端口 1455(OpenAI OAuth 要求) let port = OPENAI_OAUTH_CALLBACK_PORT; - let listener = TcpListener::bind(format!("127.0.0.1:{}", port)).await.map_err(|e| { + let listener = TcpListener::bind(format!("127.0.0.1:{port}")).await.map_err(|e| { if e.kind() == std::io::ErrorKind::AddrInUse { format!( - "端口 {} 已被占用。OpenAI OAuth 要求使用固定端口 1455,请关闭占用该端口的应用后重试。", - port + "端口 {port} 已被占用。OpenAI OAuth 要求使用固定端口 1455,请关闭占用该端口的应用后重试。" ) } else { - format!("绑定端口 {} 失败: {}", port, e) + format!("绑定端口 {port} 失败: {e}") } })?; - let redirect_uri = format!( - "http://localhost:{}{}", - OPENAI_OAUTH_CALLBACK_PORT, OPENAI_OAUTH_CALLBACK_PATH - ); + let redirect_uri = + format!("http://localhost:{OPENAI_OAUTH_CALLBACK_PORT}{OPENAI_OAUTH_CALLBACK_PATH}"); let redirect_uri_clone = redirect_uri.clone(); // 生成授权 URL(不再传入 port 参数) @@ -2256,7 +2248,7 @@ pub async fn start_codex_oauth_server_and_get_url() -> Result< if let Some(err) = error { let html = CODEX_OAUTH_ERROR_HTML.replace("ERROR_PLACEHOLDER", err); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("OAuth 错误: {}", err))); + let _ = sender.send(Err(format!("OAuth 错误: {err}"))); } return Html(html); } @@ -2337,9 +2329,9 @@ pub async fn start_codex_oauth_server_and_get_url() -> Result< if let Err(e) = std::fs::create_dir_all(&creds_dir) { let html = CODEX_OAUTH_ERROR_HTML - .replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {}", e)); + .replace("ERROR_PLACEHOLDER", &format!("创建目录失败: {e}")); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("创建目录失败: {}", e))); + let _ = sender.send(Err(format!("创建目录失败: {e}"))); } return Html(html); } @@ -2358,9 +2350,9 @@ pub async fn start_codex_oauth_server_and_get_url() -> Result< Ok(json) => json, Err(e) => { let html = CODEX_OAUTH_ERROR_HTML - .replace("ERROR_PLACEHOLDER", &format!("序列化凭证失败: {}", e)); + .replace("ERROR_PLACEHOLDER", &format!("序列化凭证失败: {e}")); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("序列化凭证失败: {}", e))); + let _ = sender.send(Err(format!("序列化凭证失败: {e}"))); } return Html(html); } @@ -2368,9 +2360,9 @@ pub async fn start_codex_oauth_server_and_get_url() -> Result< if let Err(e) = std::fs::write(&creds_file_path, &creds_json) { let html = CODEX_OAUTH_ERROR_HTML - .replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {}", e)); + .replace("ERROR_PLACEHOLDER", &format!("保存凭证失败: {e}")); if let Some(sender) = tx.lock().await.take() { - let _ = sender.send(Err(format!("保存凭证失败: {}", e))); + let _ = sender.send(Err(format!("保存凭证失败: {e}"))); } return Html(html); } diff --git a/src-tauri/src/providers/error.rs b/src-tauri/src/providers/error.rs index 7ade191b8..0a5a76f3a 100644 --- a/src-tauri/src/providers/error.rs +++ b/src-tauri/src/providers/error.rs @@ -66,31 +66,31 @@ impl ProviderError { pub fn user_friendly_message(&self) -> String { match self { ProviderError::NetworkError(msg) => { - format!("网络连接失败,请检查网络设置后重试。详情:{}", msg) + format!("网络连接失败,请检查网络设置后重试。详情:{msg}") } ProviderError::AuthenticationError(msg) => { - format!("认证失败,请重新登录。详情:{}", msg) + format!("认证失败,请重新登录。详情:{msg}") } ProviderError::TokenExpired(msg) => { - format!("Token 已过期,正在尝试刷新。详情:{}", msg) + format!("Token 已过期,正在尝试刷新。详情:{msg}") } ProviderError::ConfigurationError(msg) => { - format!("配置错误,请检查凭证设置。详情:{}", msg) + format!("配置错误,请检查凭证设置。详情:{msg}") } ProviderError::RateLimitError(msg) => { - format!("请求过于频繁,请稍后重试。详情:{}", msg) + format!("请求过于频繁,请稍后重试。详情:{msg}") } ProviderError::ServerError(msg) => { - format!("服务器暂时不可用,请稍后重试。详情:{}", msg) + format!("服务器暂时不可用,请稍后重试。详情:{msg}") } ProviderError::RequestError(msg) => { - format!("请求失败。详情:{}", msg) + format!("请求失败。详情:{msg}") } ProviderError::ParseError(msg) => { - format!("数据解析失败。详情:{}", msg) + format!("数据解析失败。详情:{msg}") } ProviderError::Unknown(msg) => { - format!("发生未知错误。详情:{}", msg) + format!("发生未知错误。详情:{msg}") } } } @@ -222,10 +222,10 @@ impl From for ProviderError { fn from(err: std::io::Error) -> Self { match err.kind() { std::io::ErrorKind::NotFound => { - ProviderError::ConfigurationError(format!("文件不存在: {}", err)) + ProviderError::ConfigurationError(format!("文件不存在: {err}")) } std::io::ErrorKind::PermissionDenied => { - ProviderError::ConfigurationError(format!("权限不足: {}", err)) + ProviderError::ConfigurationError(format!("权限不足: {err}")) } std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset diff --git a/src-tauri/src/providers/gemini.rs b/src-tauri/src/providers/gemini.rs index 3b898aefb..deecc8b65 100644 --- a/src-tauri/src/providers/gemini.rs +++ b/src-tauri/src/providers/gemini.rs @@ -550,7 +550,7 @@ impl GeminiApiKeyCredential { if pattern.contains('*') { // Simple wildcard matching let pattern = pattern.replace('*', ".*"); - regex::Regex::new(&format!("^{}$", pattern)) + regex::Regex::new(&format!("^{pattern}$")) .map(|re| re.is_match(model)) .unwrap_or(false) } else { @@ -826,7 +826,7 @@ pub fn generate_gemini_auth_url(state: &str, code_challenge: &str) -> String { .collect::>() .join("&"); - format!("https://accounts.google.com/o/oauth2/v2/auth?{}", query) + format!("https://accounts.google.com/o/oauth2/v2/auth?{query}") } /// Gemini OAuth 会话信息(用于存储 PKCE code_verifier) @@ -951,7 +951,7 @@ pub async fn exchange_gemini_code_for_token( if !resp.status().is_success() { let status = resp.status(); let body = resp.text().await.unwrap_or_default(); - return Err(format!("Token 交换失败: {} - {}", status, body).into()); + return Err(format!("Token 交换失败: {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; @@ -965,7 +965,7 @@ pub async fn fetch_gemini_user_email( ) -> Result, Box> { let resp = client .get("https://www.googleapis.com/oauth2/v2/userinfo") - .header("Authorization", format!("Bearer {}", access_token)) + .header("Authorization", format!("Bearer {access_token}")) .send() .await?; @@ -986,10 +986,9 @@ pub async fn fetch_gemini_project_id( let resp = client .post(format!( - "{}/{CODE_ASSIST_API_VERSION}:loadCodeAssist", - CODE_ASSIST_ENDPOINT + "{CODE_ASSIST_ENDPOINT}/{CODE_ASSIST_API_VERSION}:loadCodeAssist" )) - .header("Authorization", format!("Bearer {}", access_token)) + .header("Authorization", format!("Bearer {access_token}")) .header("Content-Type", "application/json") .json(&serde_json::json!({ "cloudaicompanionProject": "", @@ -1142,7 +1141,7 @@ pub async fn start_gemini_oauth_server_and_get_url() -> Result< let mut bound_port = 0; for port in ports_to_try { - match TcpListener::bind(format!("127.0.0.1:{}", port)).await { + match TcpListener::bind(format!("127.0.0.1:{port}")).await { Ok(l) => { bound_port = l.local_addr()?.port(); listener = Some(l); @@ -1187,7 +1186,7 @@ pub async fn start_gemini_oauth_server_and_get_url() -> Result< .get("error_description") .map(|s| s.as_str()) .unwrap_or("未知错误"); - let error_msg = format!("{}: {}", err, error_desc); + let error_msg = format!("{err}: {error_desc}"); tracing::error!("[Gemini OAuth] 授权失败: {}", error_msg); if let Some(tx) = tx.lock().await.take() { @@ -1289,18 +1288,18 @@ pub async fn start_gemini_oauth_server_and_get_url() -> Result< let email_display = email.unwrap_or_else(|| "未知邮箱".to_string()); let project_display = project_id - .map(|p| format!("

Project ID: {}

", p)) + .map(|p| format!("

Project ID: {p}

")) .unwrap_or_default(); let html = GEMINI_OAUTH_SUCCESS_HTML .replace("EMAIL_PLACEHOLDER", &email_display) .replace( "\n", - &format!("{}\n", project_display), + &format!("{project_display}\n"), ); Html(html) } Err(e) => { - let error_msg = format!("保存凭证失败: {}", e); + let error_msg = format!("保存凭证失败: {e}"); tracing::error!("[Gemini OAuth] {}", error_msg); if let Some(tx) = tx.lock().await.take() { @@ -1314,7 +1313,7 @@ pub async fn start_gemini_oauth_server_and_get_url() -> Result< } } Err(e) => { - let error_msg = format!("Token 交换失败: {}", e); + let error_msg = format!("Token 交换失败: {e}"); tracing::error!("[Gemini OAuth] {}", error_msg); if let Some(tx) = tx.lock().await.take() { @@ -1333,7 +1332,7 @@ pub async fn start_gemini_oauth_server_and_get_url() -> Result< let server_future = async move { axum::serve(listener, app) .await - .map_err(|e| format!("服务器错误: {}", e)) + .map_err(|e| format!("服务器错误: {e}")) }; // 启动服务器任务 diff --git a/src-tauri/src/providers/kiro.rs b/src-tauri/src/providers/kiro.rs index 886be36e2..8f000e155 100644 --- a/src-tauri/src/providers/kiro.rs +++ b/src-tauri/src/providers/kiro.rs @@ -49,7 +49,7 @@ pub fn generate_machine_id_from_credentials_with_uuid( let mut hasher = Sha256::new(); hasher.update(unique_key.as_bytes()); let result = hasher.finalize(); - format!("{:x}", result) + format!("{result:x}") } /// 获取系统运行时信息 @@ -65,7 +65,7 @@ fn get_system_runtime_info() -> (String, String) { .and_then(|o| String::from_utf8(o.stdout).ok()) .map(|s| s.trim().to_string()) .unwrap_or_else(|| "14.0".to_string()); - format!("macos#{}", version) + format!("macos#{version}") } else if cfg!(target_os = "linux") { // Linux: 获取内核版本 let version = std::process::Command::new("uname") @@ -75,7 +75,7 @@ fn get_system_runtime_info() -> (String, String) { .and_then(|o| String::from_utf8(o.stdout).ok()) .map(|s| s.trim().to_string()) .unwrap_or_else(|| "5.15.0".to_string()); - format!("linux#{}", version) + format!("linux#{version}") } else if cfg!(target_os = "windows") { // Windows: 使用固定版本(实际应该获取真实版本) "windows#10.0".to_string() @@ -108,7 +108,7 @@ fn get_device_fingerprint() -> String { let mut hasher = Sha256::new(); hasher.update(raw_id.as_bytes()); let result = hasher.finalize(); - format!("{:x}", result) + format!("{result:x}") } /// 获取原始 Machine ID(未哈希) @@ -305,7 +305,7 @@ impl KiroProvider { // 如果有 clientIdHash,尝试加载对应的 client_id 和 client_secret if let Some(hash) = &merged.client_id_hash { - let hash_file_path = dir.join(format!("{}.json", hash)); + let hash_file_path = dir.join(format!("{hash}.json")); tracing::info!( "[KIRO] 检查 clientIdHash 文件: {}", hash_file_path.display() @@ -401,7 +401,7 @@ impl KiroProvider { ); merge_credentials(&mut merged, &creds); } else { - return Err(format!("凭证文件不存在: {:?}", path).into()); + return Err(format!("凭证文件不存在: {path:?}").into()); } // 如果副本文件中已有 client_id/client_secret,直接使用(方案B:完全独立) @@ -422,7 +422,7 @@ impl KiroProvider { tracing::info!( "[KIRO] 副本文件缺少 client_id/client_secret,尝试从 clientIdHash 文件读取" ); - let hash_file_path = aws_sso_cache_dir.join(format!("{}.json", hash)); + let hash_file_path = aws_sso_cache_dir.join(format!("{hash}.json")); if tokio::fs::try_exists(&hash_file_path) .await @@ -568,7 +568,7 @@ impl KiroProvider { /// 从凭证文件中提取 region 信息的静态方法,供健康检查服务使用 pub fn extract_region_from_creds(creds_content: &str) -> Result { let creds: serde_json::Value = - serde_json::from_str(creds_content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(creds_content).map_err(|e| format!("解析凭证失败: {e}"))?; let region = creds["region"].as_str().unwrap_or("us-east-1").to_string(); @@ -633,8 +633,7 @@ impl KiroProvider { // 安全修复:不打印 token 内容,只打印长度 tracing::error!("[KIRO] 检测到 refreshToken 被截断!长度: {}", token_len); return Err(format!( - "refreshToken 已被截断(长度: {} 字符)。\n\n⚠️ 这通常是 Kiro IDE 为了防止凭证被第三方工具使用而故意截断的。\n\n💡 解决方案:\n1. 使用 Kir-Manager 工具获取完整的凭证\n2. 或者使用其他方式获取未截断的凭证文件\n3. 正常的 refreshToken 长度应该在 500+ 字符", - token_len + "refreshToken 已被截断(长度: {token_len} 字符)。\n\n⚠️ 这通常是 Kiro IDE 为了防止凭证被第三方工具使用而故意截断的。\n\n💡 解决方案:\n1. 使用 Kir-Manager 工具获取完整的凭证\n2. 或者使用其他方式获取未截断的凭证文件\n3. 正常的 refreshToken 长度应该在 500+ 字符" )); } @@ -709,8 +708,7 @@ impl KiroProvider { }; return Err(format!( - "IdC 认证配置不完整:缺少 {}。\n\n⚠️ 注意:IdC 凭证的 refreshToken 无法用于 Social 认证,必须提供完整的 IdC 配置。\n\n💡 解决方案:\n1. 删除当前凭证\n2. 重新从 Kiro IDE 获取最新的凭证文件(确保完成完整的 SSO 登录流程)\n3. 确保 ~/.aws/sso/cache/ 目录下有对应的 clientIdHash 文件\n4. 重新添加凭证到 ProxyCast", - missing + "IdC 认证配置不完整:缺少 {missing}。\n\n⚠️ 注意:IdC 凭证的 refreshToken 无法用于 Social 认证,必须提供完整的 IdC 配置。\n\n💡 解决方案:\n1. 删除当前凭证\n2. 重新从 Kiro IDE 获取最新的凭证文件(确保完成完整的 SSO 登录流程)\n3. 确保 ~/.aws/sso/cache/ 目录下有对应的 clientIdHash 文件\n4. 重新添加凭证到 ProxyCast" ).into()); } let refresh_url = self.get_refresh_url(); @@ -764,7 +762,7 @@ impl KiroProvider { .header("Host", "oidc.us-east-1.amazonaws.com") .header( "x-amz-user-agent", - format!("aws-sdk-js/3.738.0 ua/2.1 os/other lang/js api/sso-oidc#3.738.0 m/E KiroIDE-{}-{}", kiro_version, machine_id), + format!("aws-sdk-js/3.738.0 ua/2.1 os/other lang/js api/sso-oidc#3.738.0 m/E KiroIDE-{kiro_version}-{machine_id}"), ) .header("User-Agent", "node") .header("Accept", "*/*") @@ -779,10 +777,7 @@ impl KiroProvider { // Social 认证的 Headers(参考 Kir-Manager) self.client .post(&refresh_url) - .header( - "User-Agent", - format!("KiroIDE-{}-{}", kiro_version, machine_id), - ) + .header("User-Agent", format!("KiroIDE-{kiro_version}-{machine_id}")) .header("Accept", "application/json, text/plain, */*") .header("Accept-Encoding", "br, gzip, deflate") .header("Content-Type", "application/json") @@ -806,15 +801,15 @@ impl KiroProvider { let error_msg = match status.as_u16() { 401 => { if body_text.contains("Bad credentials") || body_text.contains("invalid") { - format!("OAuth 凭证已过期或无效,需要重新认证。\n💡 解决方案:\n1. 删除当前 OAuth 凭证\n2. 重新添加 OAuth 凭证\n3. 确保使用最新的凭证文件\n\n技术详情:{} {}", status, body_text) + format!("OAuth 凭证已过期或无效,需要重新认证。\n💡 解决方案:\n1. 删除当前 OAuth 凭证\n2. 重新添加 OAuth 凭证\n3. 确保使用最新的凭证文件\n\n技术详情:{status} {body_text}") } else { - format!("认证失败,Token 可能已过期。\n💡 解决方案:\n1. 检查 AWS 账户状态\n2. 重新生成 OAuth 凭证\n3. 确保凭证文件格式正确\n\n技术详情:{} {}", status, body_text) + format!("认证失败,Token 可能已过期。\n💡 解决方案:\n1. 检查 AWS 账户状态\n2. 重新生成 OAuth 凭证\n3. 确保凭证文件格式正确\n\n技术详情:{status} {body_text}") } } - 403 => format!("权限不足,无法刷新 Token。\n💡 解决方案:\n1. 检查 AWS 账户权限\n2. 确保 OAuth 应用配置正确\n3. 联系管理员检查权限设置\n\n技术详情:{} {}", status, body_text), - 429 => format!("请求过于频繁,已被限流。\n💡 解决方案:\n1. 等待 5-10 分钟后重试\n2. 减少 Token 刷新频率\n3. 检查是否有其他程序在同时使用\n\n技术详情:{} {}", status, body_text), - 500..=599 => format!("服务器错误,AWS OAuth 服务暂时不可用。\n💡 解决方案:\n1. 稍后重试(通常几分钟后恢复)\n2. 检查 AWS 服务状态页面\n3. 如持续失败,联系 AWS 支持\n\n技术详情:{} {}", status, body_text), - _ => format!("Token 刷新失败。\n💡 解决方案:\n1. 检查网络连接\n2. 确认凭证文件完整性\n3. 尝试重新添加凭证\n\n技术详情:{} {}", status, body_text) + 403 => format!("权限不足,无法刷新 Token。\n💡 解决方案:\n1. 检查 AWS 账户权限\n2. 确保 OAuth 应用配置正确\n3. 联系管理员检查权限设置\n\n技术详情:{status} {body_text}"), + 429 => format!("请求过于频繁,已被限流。\n💡 解决方案:\n1. 等待 5-10 分钟后重试\n2. 减少 Token 刷新频率\n3. 检查是否有其他程序在同时使用\n\n技术详情:{status} {body_text}"), + 500..=599 => format!("服务器错误,AWS OAuth 服务暂时不可用。\n💡 解决方案:\n1. 稍后重试(通常几分钟后恢复)\n2. 检查 AWS 服务状态页面\n3. 如持续失败,联系 AWS 支持\n\n技术详情:{status} {body_text}"), + _ => format!("Token 刷新失败。\n💡 解决方案:\n1. 检查网络连接\n2. 确认凭证文件完整性\n3. 尝试重新添加凭证\n\n技术详情:{status} {body_text}") }; return Err(error_msg.into()); diff --git a/src-tauri/src/providers/openai_custom.rs b/src-tauri/src/providers/openai_custom.rs index fcb37a8db..5cb924ace 100644 --- a/src-tauri/src/providers/openai_custom.rs +++ b/src-tauri/src/providers/openai_custom.rs @@ -95,10 +95,10 @@ impl OpenAICustomProvider { if has_version { // 已有版本号,直接拼接 endpoint - format!("{}/{}", base, endpoint) + format!("{base}/{endpoint}") } else { // 没有版本号,添加 /v1 - format!("{}/v1/{}", base, endpoint) + format!("{base}/v1/{endpoint}") } } @@ -125,9 +125,9 @@ impl OpenAICustomProvider { .unwrap_or(false); if has_version { - format!("{}/{}", base, endpoint) + format!("{base}/{endpoint}") } else { - format!("{}/v1/{}", base, endpoint) + format!("{base}/v1/{endpoint}") } } @@ -136,7 +136,7 @@ impl OpenAICustomProvider { let base = base.trim(); let mut url = Url::parse(base) - .or_else(|_| Url::parse(&format!("http://{}", base))) + .or_else(|_| Url::parse(&format!("http://{base}"))) .ok()?; let path = url.path().trim_end_matches('/'); @@ -212,7 +212,7 @@ impl OpenAICustomProvider { ); for url in &urls { - eprintln!("[OPENAI_CUSTOM] call_api trying URL: {}", url); + eprintln!("[OPENAI_CUSTOM] call_api trying URL: {url}"); let resp = self .client .post(url) @@ -243,7 +243,7 @@ impl OpenAICustomProvider { let url = self.build_url("chat/completions"); - eprintln!("[OPENAI_CUSTOM] chat_completions URL: {}", url); + eprintln!("[OPENAI_CUSTOM] chat_completions URL: {url}"); eprintln!( "[OPENAI_CUSTOM] chat_completions base_url: {}", self.get_base_url() @@ -289,7 +289,7 @@ impl OpenAICustomProvider { let mut resp: Option = None; for url in urls { - eprintln!("[OPENAI_CUSTOM] list_models URL: {}", url); + eprintln!("[OPENAI_CUSTOM] list_models URL: {url}"); tried_urls.push(url.clone()); let r = self .client @@ -309,7 +309,7 @@ impl OpenAICustomProvider { if !resp.status().is_success() { let status = resp.status(); let body = resp.text().await.unwrap_or_default(); - eprintln!("[OPENAI_CUSTOM] list_models 失败: {} - {}", status, body); + eprintln!("[OPENAI_CUSTOM] list_models 失败: {status} - {body}"); return Err(format!( "Failed to list models: {status} - {body} (tried: {})", tried_urls.join(", ") diff --git a/src-tauri/src/providers/tests.rs b/src-tauri/src/providers/tests.rs index 839daf7ee..628c71e72 100644 --- a/src-tauri/src/providers/tests.rs +++ b/src-tauri/src/providers/tests.rs @@ -215,7 +215,7 @@ proptest! { model_suffix in "[a-z0-9\\-]{1,10}", ) { // GPT models should be supported by Codex - let gpt_model = format!("gpt-{}", model_suffix); + let gpt_model = format!("gpt-{model_suffix}"); prop_assert!( CodexProvider::supports_model(&gpt_model), "Codex should support GPT model: {}", @@ -239,7 +239,7 @@ proptest! { o_variant in prop_oneof![Just("o1"), Just("o3"), Just("o4")], suffix in prop_oneof![Just(""), Just("-preview"), Just("-mini")], ) { - let model = format!("{}{}", o_variant, suffix); + let model = format!("{o_variant}{suffix}"); prop_assert!( CodexProvider::supports_model(&model), "Codex should support O-series model: {}", @@ -255,7 +255,7 @@ proptest! { prefix in "[a-z]{0,5}", suffix in "[a-z0-9\\-]{0,5}", ) { - let model = format!("{}codex{}", prefix, suffix); + let model = format!("{prefix}codex{suffix}"); prop_assert!( CodexProvider::supports_model(&model), "Codex should support model containing 'codex': {}", @@ -278,7 +278,7 @@ proptest! { ], ) { prop_assert!( - !CodexProvider::supports_model(&model), + !CodexProvider::supports_model(model), "Codex should NOT support non-GPT model: {}", model ); @@ -297,7 +297,7 @@ proptest! { ], ) { let provider = VertexProvider::with_config("test-api-key".to_string(), None) - .with_model_alias(&alias, &upstream_model); + .with_model_alias(&alias, upstream_model); // Alias should resolve to upstream model let resolved = provider.resolve_model_alias(&alias); @@ -310,7 +310,7 @@ proptest! { ); // Non-alias should return as-is - let non_alias = format!("non-alias-{}", alias); + let non_alias = format!("non-alias-{alias}"); let non_alias_clone = non_alias.clone(); let resolved_non_alias = provider.resolve_model_alias(&non_alias); prop_assert_eq!( @@ -340,7 +340,7 @@ proptest! { ); // Non-configured model should not be an alias - let non_alias = format!("not-{}", alias); + let non_alias = format!("not-{alias}"); prop_assert!( !provider.is_alias(&non_alias), "'{}' should NOT be recognized as an alias", @@ -458,7 +458,7 @@ proptest! { ); // A different model should be supported - let different_model = format!("{}-different", model); + let different_model = format!("{model}-different"); prop_assert!( cred.supports_model(&different_model), "Model '{}' should be supported (not matching exact pattern '{}')", @@ -475,8 +475,8 @@ proptest! { prefix in "[a-z]{3,8}-[0-9]\\.[0-9]-", suffix in "[a-z]{3,8}", ) { - let pattern = format!("{}*", prefix); - let matching_model = format!("{}{}", prefix, suffix); + let pattern = format!("{prefix}*"); + let matching_model = format!("{prefix}{suffix}"); let cred = GeminiApiKeyCredential::new("test-id".to_string(), "test-key".to_string()) .with_excluded_models(vec![pattern.clone()]); @@ -490,7 +490,7 @@ proptest! { ); // Model not matching prefix should be supported - let non_matching_model = format!("other-{}", suffix); + let non_matching_model = format!("other-{suffix}"); prop_assert!( cred.supports_model(&non_matching_model), "Model '{}' should be supported (not matching prefix pattern '{}')", @@ -507,8 +507,8 @@ proptest! { prefix in "[a-z]{3,8}", suffix in "-[a-z]{3,8}", ) { - let pattern = format!("*{}", suffix); - let matching_model = format!("{}{}", prefix, suffix); + let pattern = format!("*{suffix}"); + let matching_model = format!("{prefix}{suffix}"); let cred = GeminiApiKeyCredential::new("test-id".to_string(), "test-key".to_string()) .with_excluded_models(vec![pattern.clone()]); @@ -522,7 +522,7 @@ proptest! { ); // Model not matching suffix should be supported - let non_matching_model = format!("{}-other", prefix); + let non_matching_model = format!("{prefix}-other"); prop_assert!( cred.supports_model(&non_matching_model), "Model '{}' should be supported (not matching suffix pattern '{}')", @@ -538,8 +538,8 @@ proptest! { fn test_model_exclusion_contains_wildcard( middle in "[a-z]{3,6}", ) { - let pattern = format!("*{}*", middle); - let matching_model = format!("prefix-{}-suffix", middle); + let pattern = format!("*{middle}*"); + let matching_model = format!("prefix-{middle}-suffix"); let cred = GeminiApiKeyCredential::new("test-id".to_string(), "test-key".to_string()) .with_excluded_models(vec![pattern.clone()]); @@ -610,7 +610,7 @@ proptest! { ); // Prefix match should be excluded - let prefix_model = format!("{}test", prefix); + let prefix_model = format!("{prefix}test"); prop_assert!( !cred.supports_model(&prefix_model), "Model '{}' should be excluded by prefix pattern", @@ -618,7 +618,7 @@ proptest! { ); // Suffix match should be excluded - let suffix_model = format!("test{}", suffix); + let suffix_model = format!("test{suffix}"); prop_assert!( !cred.supports_model(&suffix_model), "Model '{}' should be excluded by suffix pattern", @@ -677,7 +677,7 @@ proptest! { model in "[a-z]{3,8}-[0-9]\\.[0-9]-[a-z]{3,6}", action in prop_oneof![Just("generateContent"), Just("streamGenerateContent"), Just("countTokens")], ) { - let custom_base_url = format!("https://{}.example.{}", custom_host, custom_domain); + let custom_base_url = format!("https://{custom_host}.example.{custom_domain}"); // Create credential with custom base URL let cred_with_custom = GeminiApiKeyCredential::new("test-id".to_string(), "test-key".to_string()) @@ -692,8 +692,8 @@ proptest! { ); // build_api_url() should use the custom base URL - let api_url = cred_with_custom.build_api_url(&model, &action); - let expected_url = format!("{}/v1beta/models/{}:{}", custom_base_url, model, action); + let api_url = cred_with_custom.build_api_url(&model, action); + let expected_url = format!("{custom_base_url}/v1beta/models/{model}:{action}"); // Verify the URL starts with the custom base URL prop_assert!( @@ -731,8 +731,8 @@ proptest! { ); // build_api_url() should use the default base URL - let api_url = cred_default.build_api_url(&model, &action); - let expected_url = format!("{}/v1beta/models/{}:{}", GEMINI_API_BASE_URL, model, action); + let api_url = cred_default.build_api_url(&model, action); + let expected_url = format!("{GEMINI_API_BASE_URL}/v1beta/models/{model}:{action}"); // Verify the URL starts with the default base URL prop_assert!( @@ -771,7 +771,7 @@ proptest! { ); // build_api_url() should use the default base URL - let api_url = cred.build_api_url(&model, &action); + let api_url = cred.build_api_url(&model, action); prop_assert!( api_url.starts_with(GEMINI_API_BASE_URL), "API URL should start with default base URL when base_url is None" @@ -788,7 +788,7 @@ proptest! { ) { // Note: The current implementation does NOT strip trailing slashes, // so we test the actual behavior (URL will have double slash if trailing slash provided) - let custom_base_url_no_slash = format!("https://{}.example.com", custom_host); + let custom_base_url_no_slash = format!("https://{custom_host}.example.com"); let cred = GeminiApiKeyCredential::new("test-id".to_string(), "test-key".to_string()) .with_base_url(Some(custom_base_url_no_slash.clone())); @@ -796,7 +796,7 @@ proptest! { let api_url = cred.build_api_url(&model, "generateContent"); // URL should be properly formed with the custom base URL - let expected_url = format!("{}/v1beta/models/{}:generateContent", custom_base_url_no_slash, model); + let expected_url = format!("{custom_base_url_no_slash}/v1beta/models/{model}:generateContent"); prop_assert_eq!( api_url, expected_url, @@ -813,8 +813,8 @@ proptest! { host2 in "[a-z]{3,8}", model in "[a-z]{3,8}-[0-9]\\.[0-9]-[a-z]{3,6}", ) { - let base_url_1 = format!("https://{}.api.com", host1); - let base_url_2 = format!("https://{}.api.io", host2); + let base_url_1 = format!("https://{host1}.api.com"); + let base_url_2 = format!("https://{host2}.api.io"); let cred1 = GeminiApiKeyCredential::new("cred-1".to_string(), "key-1".to_string()) .with_base_url(Some(base_url_1.clone())); diff --git a/src-tauri/src/providers/vertex.rs b/src-tauri/src/providers/vertex.rs index 52b8966a5..d99d2cfba 100644 --- a/src-tauri/src/providers/vertex.rs +++ b/src-tauri/src/providers/vertex.rs @@ -184,7 +184,7 @@ impl VertexProvider { .unwrap_or("gemini-2.0-flash"); // Vertex AI uses a different URL pattern - let url = format!("{}/models/{}:generateContent", base_url, model); + let url = format!("{base_url}/models/{model}:generateContent"); let resp = self .client @@ -223,7 +223,7 @@ impl VertexProvider { .unwrap_or("gemini-2.0-flash"); // Streaming endpoint - let url = format!("{}/models/{}:streamGenerateContent", base_url, model); + let url = format!("{base_url}/models/{model}:streamGenerateContent"); let resp = self .client @@ -246,7 +246,7 @@ impl VertexProvider { .ok_or("Vertex AI API key not configured")?; let base_url = self.get_base_url(); - let url = format!("{}/models", base_url); + let url = format!("{base_url}/models"); let resp = self .client @@ -258,7 +258,7 @@ impl VertexProvider { if !resp.status().is_success() { let status = resp.status(); let body = resp.text().await.unwrap_or_default(); - return Err(format!("Failed to list models: {} - {}", status, body).into()); + return Err(format!("Failed to list models: {status} - {body}").into()); } let data: serde_json::Value = resp.json().await?; diff --git a/src-tauri/src/router/provider_router.rs b/src-tauri/src/router/provider_router.rs index b2aa5c7af..02c2c817e 100644 --- a/src-tauri/src/router/provider_router.rs +++ b/src-tauri/src/router/provider_router.rs @@ -204,7 +204,7 @@ impl ProviderRouter { }; if route.route_type == RouteType::Default { - format!("{}/v1/{}", base_url, endpoint) + format!("{base_url}/v1/{endpoint}") } else { let selector = route .credential_name @@ -212,7 +212,7 @@ impl ProviderRouter { .or(route.credential_uuid.as_ref()) .map(|s| s.as_str()) .unwrap_or("unknown"); - format!("{}/{}/v1/{}", base_url, selector, endpoint) + format!("{base_url}/{selector}/v1/{endpoint}") } } } diff --git a/src-tauri/src/router/route_registry.rs b/src-tauri/src/router/route_registry.rs index bf356a9e6..d9993525d 100644 --- a/src-tauri/src/router/route_registry.rs +++ b/src-tauri/src/router/route_registry.rs @@ -65,7 +65,7 @@ impl RegisteredRoute { /// 创建凭证选择器路由 pub fn credential_selector(credential_uuid: &str, provider_type: &str) -> Self { Self { - path_pattern: format!("/{}/v1/{{endpoint}}", credential_uuid), + path_pattern: format!("/{credential_uuid}/v1/{{endpoint}}"), route_type: RouteType::CredentialSelector, provider_type: Some(provider_type.to_string()), credential_uuid: Some(credential_uuid.to_string()), diff --git a/src-tauri/src/screenshot/capture.rs b/src-tauri/src/screenshot/capture.rs index d30a50504..9dcd97f72 100644 --- a/src-tauri/src/screenshot/capture.rs +++ b/src-tauri/src/screenshot/capture.rs @@ -53,7 +53,7 @@ pub async fn start_capture(_app: &AppHandle) -> Result { // 生成临时文件路径 let temp_dir = std::env::temp_dir(); let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S_%3f"); - let filename = format!("proxycast_screenshot_{}.png", timestamp); + let filename = format!("proxycast_screenshot_{timestamp}.png"); let temp_path = temp_dir.join(&filename); debug!("截图临时文件路径: {:?}", temp_path); @@ -123,7 +123,7 @@ async fn capture_macos(output_path: &PathBuf) -> Result<(), CaptureError> { let output = Command::new("screencapture") .args(["-i", "-x", output_path.to_str().unwrap()]) .output() - .map_err(|e| CaptureError::SystemError(format!("执行 screencapture 失败: {}", e)))?; + .map_err(|e| CaptureError::SystemError(format!("执行 screencapture 失败: {e}")))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); @@ -133,8 +133,7 @@ async fn capture_macos(output_path: &PathBuf) -> Result<(), CaptureError> { } error!("screencapture 命令失败: {}", stderr); return Err(CaptureError::SystemError(format!( - "screencapture 失败: {}", - stderr + "screencapture 失败: {stderr}" ))); } @@ -237,7 +236,7 @@ mod tests { fn test_temp_path_generation() { let temp_dir = std::env::temp_dir(); let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S_%3f"); - let filename = format!("proxycast_screenshot_{}.png", timestamp); + let filename = format!("proxycast_screenshot_{timestamp}.png"); let temp_path = temp_dir.join(&filename); assert!(temp_path diff --git a/src-tauri/src/screenshot/config.rs b/src-tauri/src/screenshot/config.rs index 461042931..03f9990cc 100644 --- a/src-tauri/src/screenshot/config.rs +++ b/src-tauri/src/screenshot/config.rs @@ -96,7 +96,7 @@ pub async fn save_experimental_config( config_manager .save_config(&config) .await - .map_err(|e| ConfigError::LoadFailed(e))?; + .map_err(ConfigError::LoadFailed)?; debug!("实验室功能配置已保存"); Ok(()) diff --git a/src-tauri/src/screenshot/shortcut.rs b/src-tauri/src/screenshot/shortcut.rs index 1efb21594..f428079dc 100644 --- a/src-tauri/src/screenshot/shortcut.rs +++ b/src-tauri/src/screenshot/shortcut.rs @@ -57,9 +57,9 @@ pub fn validate(shortcut: &str) -> Result<(), ShortcutError> { } // 尝试解析快捷键以验证格式 - shortcut.parse::().map_err(|e| { - ShortcutError::InvalidFormat(format!("无法解析快捷键 '{}': {}", shortcut, e)) - })?; + shortcut + .parse::() + .map_err(|e| ShortcutError::InvalidFormat(format!("无法解析快捷键 '{shortcut}': {e}")))?; debug!("快捷键格式验证通过: {}", shortcut); Ok(()) @@ -84,13 +84,13 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), ShortcutError // 解析快捷键 let shortcut: Shortcut = shortcut_str .parse() - .map_err(|e| ShortcutError::ParseFailed(format!("{}", e)))?; + .map_err(|e| ShortcutError::ParseFailed(format!("{e}")))?; // 获取全局快捷键管理器 let global_shortcut = app.global_shortcut(); // 检查快捷键是否已被注册 - let is_already_registered = global_shortcut.is_registered(shortcut.clone()); + let is_already_registered = global_shortcut.is_registered(shortcut); info!( "快捷键 {} 是否已注册: {}", shortcut_str, is_already_registered @@ -101,7 +101,7 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), ShortcutError // 如果是我们自己注册的,先注销 if IS_REGISTERED.load(Ordering::SeqCst) { info!("尝试注销已有的快捷键"); - if let Err(e) = global_shortcut.unregister(shortcut.clone()) { + if let Err(e) = global_shortcut.unregister(shortcut) { error!("注销已有快捷键失败: {}", e); } } else { @@ -115,7 +115,7 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), ShortcutError // 注册快捷键 info!("开始注册快捷键回调..."); global_shortcut - .on_shortcut(shortcut.clone(), move |_app, _shortcut, event| { + .on_shortcut(shortcut, move |_app, _shortcut, event| { if event.state == ShortcutState::Pressed { info!("截图快捷键被触发"); handle_shortcut_triggered(&app_clone); @@ -123,7 +123,7 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), ShortcutError }) .map_err(|e| { error!("注册快捷键失败: {}", e); - ShortcutError::RegisterFailed(format!("{}", e)) + ShortcutError::RegisterFailed(format!("{e}")) })?; // 更新状态 @@ -151,14 +151,14 @@ pub fn unregister(app: &AppHandle) -> Result<(), ShortcutError> { let shortcut: Shortcut = shortcut_str .parse() - .map_err(|e| ShortcutError::ParseFailed(format!("{}", e)))?; + .map_err(|e| ShortcutError::ParseFailed(format!("{e}")))?; let global_shortcut = app.global_shortcut(); - if global_shortcut.is_registered(shortcut.clone()) { + if global_shortcut.is_registered(shortcut) { global_shortcut .unregister(shortcut) - .map_err(|e| ShortcutError::UnregisterFailed(format!("{}", e)))?; + .map_err(|e| ShortcutError::UnregisterFailed(format!("{e}")))?; } // 更新状态 diff --git a/src-tauri/src/screenshot/window.rs b/src-tauri/src/screenshot/window.rs index a52e5de62..2adad44a6 100644 --- a/src-tauri/src/screenshot/window.rs +++ b/src-tauri/src/screenshot/window.rs @@ -146,7 +146,7 @@ pub fn open_floating_window(app: &AppHandle, image_path: &Path) -> Result<(), Wi // 构建窗口 URL,包含图片路径参数 let image_path_str = image_path.to_str().unwrap_or(""); let encoded_path = urlencoding::encode(image_path_str); - let url = format!("/smart-input?image={}", encoded_path); + let url = format!("/smart-input?image={encoded_path}"); debug!("悬浮窗口 URL: {}", url); @@ -180,20 +180,20 @@ pub fn open_floating_window(app: &AppHandle, image_path: &Path) -> Result<(), Wi } // 使用 JavaScript 导航到新的 URL(更新图片路径) - let js = format!("window.location.href = '{}';", url); + let js = format!("window.location.href = '{url}';"); window .eval(&js) - .map_err(|e| WindowError::OperationFailed(format!("导航失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("导航失败: {e}")))?; // 显示窗口 window .show() - .map_err(|e| WindowError::OperationFailed(format!("显示窗口失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("显示窗口失败: {e}")))?; // 聚焦窗口 window .set_focus() - .map_err(|e| WindowError::OperationFailed(format!("聚焦窗口失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("聚焦窗口失败: {e}")))?; return Ok(()); } @@ -216,7 +216,7 @@ pub fn open_floating_window(app: &AppHandle, image_path: &Path) -> Result<(), Wi .focused(true) .transparent(true) .build() - .map_err(|e| WindowError::CreateFailed(format!("{}", e)))?; + .map_err(|e| WindowError::CreateFailed(format!("{e}")))?; // macOS: 设置窗口和 webview 背景透明 #[cfg(target_os = "macos")] @@ -254,7 +254,7 @@ pub fn close_floating_window(app: &AppHandle) -> Result<(), WindowError> { if let Some(window) = app.get_webview_window(FLOATING_WINDOW_LABEL) { window .close() - .map_err(|e| WindowError::OperationFailed(format!("关闭窗口失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("关闭窗口失败: {e}")))?; info!("悬浮窗口已关闭"); } else { debug!("悬浮窗口不存在,无需关闭"); @@ -287,7 +287,7 @@ pub fn focus_floating_window(app: &AppHandle) -> Result<(), WindowError> { if let Some(window) = app.get_webview_window(FLOATING_WINDOW_LABEL) { window .set_focus() - .map_err(|e| WindowError::OperationFailed(format!("聚焦窗口失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("聚焦窗口失败: {e}")))?; Ok(()) } else { Err(WindowError::NotFound(FLOATING_WINDOW_LABEL.to_string())) @@ -309,7 +309,7 @@ pub fn open_floating_window_with_text(app: &AppHandle, text: &str) -> Result<(), // 构建窗口 URL,包含文本参数 let encoded_text = urlencoding::encode(text); - let url = format!("/smart-input?text={}", encoded_text); + let url = format!("/smart-input?text={encoded_text}"); open_floating_window_with_url(app, &url) } @@ -364,18 +364,18 @@ fn open_floating_window_with_url(app: &AppHandle, url: &str) -> Result<(), Windo } // 导航到新 URL(强制刷新) - let js = format!("window.location.replace('{}');", url); + let js = format!("window.location.replace('{url}');"); window .eval(&js) - .map_err(|e| WindowError::OperationFailed(format!("导航失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("导航失败: {e}")))?; window .show() - .map_err(|e| WindowError::OperationFailed(format!("显示窗口失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("显示窗口失败: {e}")))?; window .set_focus() - .map_err(|e| WindowError::OperationFailed(format!("聚焦窗口失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("聚焦窗口失败: {e}")))?; // 如果是语音模式,额外发送事件确保前端收到 if is_voice_mode { @@ -408,7 +408,7 @@ fn open_floating_window_with_url(app: &AppHandle, url: &str) -> Result<(), Windo .focused(true) .transparent(true) .build() - .map_err(|e| WindowError::CreateFailed(format!("{}", e)))?; + .map_err(|e| WindowError::CreateFailed(format!("{e}")))?; // macOS: 设置窗口背景透明 #[cfg(target_os = "macos")] @@ -443,7 +443,7 @@ pub fn send_voice_stop_event(app: &AppHandle) -> Result<(), WindowError> { if let Some(window) = app.get_webview_window(FLOATING_WINDOW_LABEL) { window .emit("voice-stop-recording", ()) - .map_err(|e| WindowError::OperationFailed(format!("发送停止录音事件失败: {}", e)))?; + .map_err(|e| WindowError::OperationFailed(format!("发送停止录音事件失败: {e}")))?; info!("[语音输入] 已发送停止录音事件到截图输入框"); } Ok(()) @@ -465,10 +465,7 @@ pub fn open_floating_window_with_translate( ) -> Result<(), WindowError> { info!("打开翻译模式的悬浮输入框,指令: {}", instruction_id); let encoded_instruction = urlencoding::encode(instruction_id); - let url = format!( - "/smart-input?voice=true&translate=true&instruction={}", - encoded_instruction - ); + let url = format!("/smart-input?voice=true&translate=true&instruction={encoded_instruction}"); open_floating_window_with_url(app, &url) } diff --git a/src-tauri/src/server/handlers/api.rs b/src-tauri/src/server/handlers/api.rs index 46a5984f2..e8c1d1fee 100644 --- a/src-tauri/src/server/handlers/api.rs +++ b/src-tauri/src/server/handlers/api.rs @@ -414,10 +414,11 @@ async fn check_request_intercept( return InterceptCheckResult::Continue(None); } - state.logs.write().await.add( - "info", - &format!("[INTERCEPT] 拦截请求: flow_id={}", flow_id), - ); + state + .logs + .write() + .await + .add("info", &format!("[INTERCEPT] 拦截请求: flow_id={flow_id}")); // 拦截请求 let _intercepted = state @@ -448,17 +449,14 @@ async fn check_request_intercept( InterceptAction::Cancel => { state.logs.write().await.add( "info", - &format!("[INTERCEPT] 请求被取消: flow_id={}", flow_id), + &format!("[INTERCEPT] 请求被取消: flow_id={flow_id}"), ); InterceptCheckResult::Cancelled } InterceptAction::Timeout(timeout_action) => { state.logs.write().await.add( "warn", - &format!( - "[INTERCEPT] 请求超时: flow_id={}, action={:?}", - flow_id, timeout_action - ), + &format!("[INTERCEPT] 请求超时: flow_id={flow_id}, action={timeout_action:?}"), ); match timeout_action { crate::flow_monitor::TimeoutAction::Continue => { @@ -501,10 +499,11 @@ async fn check_response_intercept( return None; } - state.logs.write().await.add( - "info", - &format!("[INTERCEPT] 拦截响应: flow_id={}", flow_id), - ); + state + .logs + .write() + .await + .add("info", &format!("[INTERCEPT] 拦截响应: flow_id={flow_id}")); // 拦截响应 let _intercepted = state @@ -535,7 +534,7 @@ async fn check_response_intercept( InterceptAction::Cancel | InterceptAction::Timeout(_) => { state.logs.write().await.add( "warn", - &format!("[INTERCEPT] 响应处理被取消或超时: flow_id={}", flow_id), + &format!("[INTERCEPT] 响应处理被取消或超时: flow_id={flow_id}"), ); None } @@ -700,10 +699,7 @@ pub async fn chat_completions( // 根据客户端类型选择 Provider // **Validates: Requirements 3.1, 3.3, 3.4** let (selected_provider, client_type) = select_provider_for_client(&headers, &state).await; - eprintln!( - "[CHAT_COMPLETIONS] 客户端类型: {}, 选择的Provider: {}", - client_type, selected_provider - ); + eprintln!("[CHAT_COMPLETIONS] 客户端类型: {client_type}, 选择的Provider: {selected_provider}"); // 记录客户端检测和 Provider 选择结果 state.logs.write().await.add( @@ -738,8 +734,7 @@ pub async fn chat_completions( // 如果指定了 X-Provider-Id,优先使用它(不降级) if let Some(ref explicit_provider_id) = provider_id_header { eprintln!( - "[CHAT_COMPLETIONS] 使用 X-Provider-Id 指定的 provider: {}", - explicit_provider_id + "[CHAT_COMPLETIONS] 使用 X-Provider-Id 指定的 provider: {explicit_provider_id}" ); let cred = state .pool_service @@ -754,14 +749,12 @@ pub async fn chat_completions( if cred.is_none() { eprintln!( - "[CHAT_COMPLETIONS] X-Provider-Id '{}' 没有可用凭证,不进行降级", - explicit_provider_id + "[CHAT_COMPLETIONS] X-Provider-Id '{explicit_provider_id}' 没有可用凭证,不进行降级" ); state.logs.write().await.add( "error", &format!( - "[ROUTE] No available credentials for explicitly specified provider '{}', refusing to fallback", - explicit_provider_id + "[ROUTE] No available credentials for explicitly specified provider '{explicit_provider_id}', refusing to fallback" ), ); // 返回错误,不降级 @@ -796,15 +789,9 @@ pub async fn chat_completions( .flatten(); if cred.is_some() { - eprintln!( - "[CHAT_COMPLETIONS] 找到凭证: provider={}", - selected_provider - ); + eprintln!("[CHAT_COMPLETIONS] 找到凭证: provider={selected_provider}"); } else { - eprintln!( - "[CHAT_COMPLETIONS] 未找到凭证: provider={}", - selected_provider - ); + eprintln!("[CHAT_COMPLETIONS] 未找到凭证: provider={selected_provider}"); } cred @@ -830,10 +817,7 @@ pub async fn chat_completions( if let Some(db) = &state.db { // 先尝试按 provider_id 直接查找 - eprintln!( - "[CHAT_COMPLETIONS] 尝试按 provider_id '{}' 直接查找凭证", - provider_id_lower - ); + eprintln!("[CHAT_COMPLETIONS] 尝试按 provider_id '{provider_id_lower}' 直接查找凭证"); match state .api_key_service @@ -853,20 +837,18 @@ pub async fn chat_completions( state.logs.write().await.add( "info", &format!( - "[ROUTE] Using API Key Provider credential by provider_id: {}", - provider_id_lower + "[ROUTE] Using API Key Provider credential by provider_id: {provider_id_lower}" ), ); found_credential = Some(cred); } Ok(None) => { eprintln!( - "[CHAT_COMPLETIONS] provider_id '{}' 未找到凭证,尝试按类型查找", - provider_id_lower + "[CHAT_COMPLETIONS] provider_id '{provider_id_lower}' 未找到凭证,尝试按类型查找" ); } Err(e) => { - eprintln!("[CHAT_COMPLETIONS] 按 provider_id 查找凭证失败: {}", e); + eprintln!("[CHAT_COMPLETIONS] 按 provider_id 查找凭证失败: {e}"); } } @@ -884,8 +866,7 @@ pub async fn chat_completions( if let Some(api_type) = api_provider_type { eprintln!( - "[CHAT_COMPLETIONS] 尝试从 API Key Provider 类型 '{:?}' 获取凭证", - api_type + "[CHAT_COMPLETIONS] 尝试从 API Key Provider 类型 '{api_type:?}' 获取凭证" ); match state.api_key_service.get_next_api_key_by_type(db, api_type) { @@ -950,12 +931,11 @@ pub async fn chat_completions( } Ok(None) => { eprintln!( - "[CHAT_COMPLETIONS] API Key Provider 类型 '{:?}' 没有可用的 API Key", - api_type + "[CHAT_COMPLETIONS] API Key Provider 类型 '{api_type:?}' 没有可用的 API Key" ); } Err(e) => { - eprintln!("[CHAT_COMPLETIONS] 从 API Key Provider 获取凭证失败: {}", e); + eprintln!("[CHAT_COMPLETIONS] 从 API Key Provider 获取凭证失败: {e}"); } } } @@ -996,12 +976,12 @@ pub async fn chat_completions( // 从凭证名称中提取 Provider 显示名称 // 凭证名称格式:Some("[降级] DeepSeek") 或 Some("DeepSeek") - let provider_display_name = cred.name.as_ref().and_then(|name| { + let provider_display_name = cred.name.as_ref().map(|name| { // 去掉 "[降级] " 前缀 if name.starts_with("[降级] ") { - Some(&name[9..]) // "[降级] " 是 9 个字节 + &name[9..] // "[降级] " 是 9 个字节 } else { - Some(name.as_str()) + name.as_str() } }); @@ -1080,10 +1060,10 @@ pub async fn chat_completions( let body_bytes = match axum::body::to_bytes(body, usize::MAX).await { Ok(bytes) => bytes, Err(e) => { - eprintln!("[CHAT_COMPLETIONS] 读取响应体失败: {}", e); + eprintln!("[CHAT_COMPLETIONS] 读取响应体失败: {e}"); // 如果读取失败,返回错误 if let Some(fid) = flow_id { - let error = FlowError::new(FlowErrorType::Network, &e.to_string()); + let error = FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(&fid, error).await; } return ( @@ -1098,12 +1078,12 @@ pub async fn chat_completions( let response_json: serde_json::Value = match serde_json::from_slice(&body_bytes) { Ok(json) => json, Err(e) => { - eprintln!("[CHAT_COMPLETIONS] 解析响应体失败: {}", e); + eprintln!("[CHAT_COMPLETIONS] 解析响应体失败: {e}"); // 如果解析失败,仍然返回原始响应 if let Some(fid) = flow_id { let error = FlowError::new( FlowErrorType::Other, - &format!("Failed to parse response: {}", e), + format!("Failed to parse response: {e}"), ); state.flow_monitor.fail_flow(&fid, error).await; } @@ -1172,7 +1152,7 @@ pub async fn chat_completions( .logs .write() .await - .add("info", &format!("[INTERCEPT] 响应被修改: flow_id={}", fid)); + .add("info", &format!("[INTERCEPT] 响应被修改: flow_id={fid}")); // 使用修改后的响应完成 Flow state @@ -1217,7 +1197,7 @@ pub async fn chat_completions( .complete_flow(&fid, Some(llm_response)) .await; - eprintln!("[FLOW_DEBUG] Flow 已完成: flow_id={}", fid); + eprintln!("[FLOW_DEBUG] Flow 已完成: flow_id={fid}"); } // 重新构建响应返回给客户端 @@ -1271,8 +1251,7 @@ pub async fn chat_completions( state.logs.write().await.add( "error", &format!( - "[ROUTE] No pool credential found for '{}' (client_type={}), and legacy mode only supports Kiro", - selected_provider, client_type + "[ROUTE] No pool credential found for '{selected_provider}' (client_type={client_type}), and legacy mode only supports Kiro" ), ); return ( @@ -1290,10 +1269,7 @@ pub async fn chat_completions( state.logs.write().await.add( "debug", - &format!( - "[ROUTE] No pool credential found for '{}', using legacy mode", - selected_provider - ), + &format!("[ROUTE] No pool credential found for '{selected_provider}', using legacy mode"), ); // 启动 Flow 捕获(legacy mode) @@ -1359,7 +1335,7 @@ pub async fn chat_completions( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::Authentication, - &format!("Token refresh failed: {e}"), + format!("Token refresh failed: {e}"), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -1483,10 +1459,11 @@ pub async fn chat_completions( .await { // 响应被修改,需要重新构建响应 - state.logs.write().await.add( - "info", - &format!("[INTERCEPT] 响应被修改: flow_id={}", fid), - ); + state + .logs + .write() + .await + .add("info", &format!("[INTERCEPT] 响应被修改: flow_id={fid}")); // 使用修改后的响应完成 Flow state @@ -1540,7 +1517,7 @@ pub async fn chat_completions( ); // 标记 Flow 失败 if let Some(fid) = &flow_id { - let error = FlowError::new(FlowErrorType::Network, &e.to_string()); + let error = FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(fid, error).await; } ( @@ -1656,8 +1633,7 @@ pub async fn chat_completions( state.logs.write().await.add( "info", &format!( - "[INTERCEPT] 响应被修改: flow_id={}", - fid + "[INTERCEPT] 响应被修改: flow_id={fid}" ), ); @@ -1712,7 +1688,7 @@ pub async fn chat_completions( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::Network, - &e.to_string(), + e.to_string(), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -1728,7 +1704,7 @@ pub async fn chat_completions( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::ServerError, - &format!("Retry failed: {}", body), + format!("Retry failed: {body}"), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -1741,7 +1717,7 @@ pub async fn chat_completions( // 标记 Flow 失败 if let Some(fid) = &flow_id { let error = - FlowError::new(FlowErrorType::Network, &e.to_string()); + FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(fid, error).await; } ( @@ -1762,7 +1738,7 @@ pub async fn chat_completions( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::Authentication, - &format!("Token refresh failed: {e}"), + format!("Token refresh failed: {e}"), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -1800,7 +1776,7 @@ pub async fn chat_completions( .add("error", &format!("API call failed: {e}")); // 标记 Flow 失败 if let Some(fid) = &flow_id { - let error = FlowError::new(FlowErrorType::Network, &e.to_string()); + let error = FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(fid, error).await; } ( @@ -1941,8 +1917,7 @@ pub async fn anthropic_messages( // 如果指定了 X-Provider-Id,优先使用它(不降级) if let Some(ref explicit_provider_id) = provider_id_header { eprintln!( - "[ANTHROPIC_MESSAGES] 使用 X-Provider-Id 指定的 provider: {}", - explicit_provider_id + "[ANTHROPIC_MESSAGES] 使用 X-Provider-Id 指定的 provider: {explicit_provider_id}" ); let cred = state .pool_service @@ -1957,14 +1932,12 @@ pub async fn anthropic_messages( if cred.is_none() { eprintln!( - "[AMP] X-Provider-Id '{}' 没有可用凭证,不进行降级", - explicit_provider_id + "[AMP] X-Provider-Id '{explicit_provider_id}' 没有可用凭证,不进行降级" ); state.logs.write().await.add( "error", &format!( - "[ROUTE] No available credentials for explicitly specified provider '{}', refusing to fallback", - explicit_provider_id + "[ROUTE] No available credentials for explicitly specified provider '{explicit_provider_id}', refusing to fallback" ), ); // 返回错误,不降级 @@ -1998,15 +1971,9 @@ pub async fn anthropic_messages( .flatten(); if cred.is_some() { - eprintln!( - "[ANTHROPIC_MESSAGES] 找到凭证: provider={}", - selected_provider - ); + eprintln!("[ANTHROPIC_MESSAGES] 找到凭证: provider={selected_provider}"); } else { - eprintln!( - "[ANTHROPIC_MESSAGES] 未找到凭证: provider={}", - selected_provider - ); + eprintln!("[ANTHROPIC_MESSAGES] 未找到凭证: provider={selected_provider}"); } cred @@ -2027,10 +1994,7 @@ pub async fn anthropic_messages( None; if let Some(db) = &state.db { - eprintln!( - "[ANTHROPIC_MESSAGES] 尝试按 provider_id '{}' 直接查找凭证", - selected_provider - ); + eprintln!("[ANTHROPIC_MESSAGES] 尝试按 provider_id '{selected_provider}' 直接查找凭证"); match state .api_key_service @@ -2050,13 +2014,10 @@ pub async fn anthropic_messages( found_credential = Some(cred); } Ok(None) => { - eprintln!( - "[ANTHROPIC_MESSAGES] provider_id '{}' 未找到凭证", - selected_provider - ); + eprintln!("[ANTHROPIC_MESSAGES] provider_id '{selected_provider}' 未找到凭证"); } Err(e) => { - eprintln!("[ANTHROPIC_MESSAGES] 查找凭证时出错: {}", e); + eprintln!("[ANTHROPIC_MESSAGES] 查找凭证时出错: {e}"); } } } @@ -2087,12 +2048,12 @@ pub async fn anthropic_messages( // 从凭证名称中提取 Provider 显示名称 // 凭证名称格式:Some("[降级] DeepSeek") 或 Some("DeepSeek") - let provider_display_name = cred.name.as_ref().and_then(|name| { + let provider_display_name = cred.name.as_ref().map(|name| { // 去掉 "[降级] " 前缀 if name.starts_with("[降级] ") { - Some(&name[9..]) // "[降级] " 是 9 个字节 + &name[9..] // "[降级] " 是 9 个字节 } else { - Some(name.as_str()) + name.as_str() } }); @@ -2205,7 +2166,7 @@ pub async fn anthropic_messages( .logs .write() .await - .add("info", &format!("[INTERCEPT] 响应被修改: flow_id={}", fid)); + .add("info", &format!("[INTERCEPT] 响应被修改: flow_id={fid}")); // 使用修改后的响应完成 Flow state @@ -2260,8 +2221,7 @@ pub async fn anthropic_messages( state.logs.write().await.add( "error", &format!( - "[ROUTE] No pool credential found for '{}' (client_type={}), and legacy mode only supports Kiro", - selected_provider, client_type + "[ROUTE] No pool credential found for '{selected_provider}' (client_type={client_type}), and legacy mode only supports Kiro" ), ); return ( @@ -2279,10 +2239,7 @@ pub async fn anthropic_messages( state.logs.write().await.add( "debug", - &format!( - "[ROUTE] No pool credential found for '{}', using legacy mode", - selected_provider - ), + &format!("[ROUTE] No pool credential found for '{selected_provider}', using legacy mode"), ); // 启动 Flow 捕获(legacy mode) @@ -2358,7 +2315,7 @@ pub async fn anthropic_messages( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::Authentication, - &format!("Token refresh failed: {e}"), + format!("Token refresh failed: {e}"), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -2479,7 +2436,7 @@ pub async fn anthropic_messages( // 响应被修改,需要重新构建响应 state.logs.write().await.add( "info", - &format!("[INTERCEPT] 流式响应被修改: flow_id={}", fid), + &format!("[INTERCEPT] 流式响应被修改: flow_id={fid}"), ); // 使用修改后的响应完成 Flow @@ -2541,10 +2498,11 @@ pub async fn anthropic_messages( .await { // 响应被修改,需要重新构建响应 - state.logs.write().await.add( - "info", - &format!("[INTERCEPT] 响应被修改: flow_id={}", fid), - ); + state + .logs + .write() + .await + .add("info", &format!("[INTERCEPT] 响应被修改: flow_id={fid}")); // 使用修改后的响应完成 Flow state @@ -2592,7 +2550,7 @@ pub async fn anthropic_messages( .add("error", &format!("[ERROR] Response body read failed: {e}")); // 标记 Flow 失败 if let Some(fid) = &flow_id { - let error = FlowError::new(FlowErrorType::Network, &e.to_string()); + let error = FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(fid, error).await; } ( @@ -2675,7 +2633,7 @@ pub async fn anthropic_messages( // 响应被修改,需要重新构建响应 state.logs.write().await.add( "info", - &format!("[INTERCEPT] 重试响应被修改: flow_id={}", fid), + &format!("[INTERCEPT] 重试响应被修改: flow_id={fid}"), ); // 使用修改后的响应完成 Flow @@ -2758,7 +2716,7 @@ pub async fn anthropic_messages( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::Network, - &e.to_string(), + e.to_string(), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -2786,7 +2744,7 @@ pub async fn anthropic_messages( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::ServerError, - &format!("Retry failed: {}", body), + format!("Retry failed: {body}"), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -2805,7 +2763,7 @@ pub async fn anthropic_messages( // 标记 Flow 失败 if let Some(fid) = &flow_id { let error = - FlowError::new(FlowErrorType::Network, &e.to_string()); + FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(fid, error).await; } ( @@ -2826,7 +2784,7 @@ pub async fn anthropic_messages( if let Some(fid) = &flow_id { let error = FlowError::new( FlowErrorType::Authentication, - &format!("Token refresh failed: {e}"), + format!("Token refresh failed: {e}"), ); state.flow_monitor.fail_flow(fid, error).await; } @@ -2878,7 +2836,7 @@ pub async fn anthropic_messages( ); // 标记 Flow 失败 if let Some(fid) = &flow_id { - let error = FlowError::new(FlowErrorType::Network, &e.to_string()); + let error = FlowError::new(FlowErrorType::Network, e.to_string()); state.flow_monitor.fail_flow(fid, error).await; } ( @@ -3060,7 +3018,7 @@ fn build_api_key_headers( headers.insert("api-key".to_string(), api_key.to_string()); } _ => { - headers.insert("Authorization".to_string(), format!("Bearer {}", api_key)); + headers.insert("Authorization".to_string(), format!("Bearer {api_key}")); } } diff --git a/src-tauri/src/server/handlers/credentials_api.rs b/src-tauri/src/server/handlers/credentials_api.rs index f5d88fcd4..1266efd06 100644 --- a/src-tauri/src/server/handlers/credentials_api.rs +++ b/src-tauri/src/server/handlers/credentials_api.rs @@ -227,7 +227,7 @@ async fn try_select_api_key_credential( // 获取 Provider 信息以确定 base_url let conn = db.lock().map_err(|e| CredentialApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; @@ -317,7 +317,7 @@ fn build_api_key_headers( headers.insert("api-key".to_string(), api_key.to_string()); } _ => { - headers.insert("Authorization".to_string(), format!("Bearer {}", api_key)); + headers.insert("Authorization".to_string(), format!("Bearer {api_key}")); } } @@ -355,7 +355,7 @@ pub async fn credentials_get_token( // 未找到凭证 Err(CredentialApiError { error: "credential_not_found".to_string(), - message: format!("未找到 UUID 为 {} 的凭证", uuid), + message: format!("未找到 UUID 为 {uuid} 的凭证"), status_code: 404, }) } @@ -370,7 +370,7 @@ async fn try_get_oauth_token( let credential = { let conn = db.lock().map_err(|e| CredentialApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; @@ -431,7 +431,7 @@ async fn try_get_oauth_token( let updated_credential = { let conn = db.lock().map_err(|e| CredentialApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; @@ -473,7 +473,7 @@ async fn try_get_api_key_token( ) -> Result, CredentialApiError> { let conn = db.lock().map_err(|e| CredentialApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; @@ -498,7 +498,7 @@ async fn try_get_api_key_token( .decrypt_api_key(&api_key_entry.api_key_encrypted) .map_err(|e| CredentialApiError { error: "decryption_error".to_string(), - message: format!("API Key 解密失败: {}", e), + message: format!("API Key 解密失败: {e}"), status_code: 500, })?; diff --git a/src-tauri/src/server/handlers/image_handler.rs b/src-tauri/src/server/handlers/image_handler.rs index 6641fb1de..a2c673129 100644 --- a/src-tauri/src/server/handlers/image_handler.rs +++ b/src-tauri/src/server/handlers/image_handler.rs @@ -89,7 +89,7 @@ pub async fn handle_image_generation( // 安全截取 prompt,避免 UTF-8 字符边界问题 let prompt_preview: String = request.prompt.chars().take(50).collect(); let prompt_display = if request.prompt.chars().count() > 50 { - format!("{}...", prompt_preview) + format!("{prompt_preview}...") } else { request.prompt.clone() }; @@ -147,7 +147,7 @@ pub async fn handle_image_generation( .logs .write() .await - .add("error", &format!("[IMAGE] 获取凭证失败: {}", e)); + .add("error", &format!("[IMAGE] 获取凭证失败: {e}")); return ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ @@ -195,7 +195,7 @@ pub async fn handle_image_generation( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); return ( StatusCode::INTERNAL_SERVER_ERROR, @@ -267,7 +267,7 @@ pub async fn handle_image_generation( .as_str() .unwrap_or("gemini-3-pro-image-preview"); - eprintln!("[IMAGE] 调用 Antigravity API: model={}", model); + eprintln!("[IMAGE] 调用 Antigravity API: model={model}"); eprintln!( "[IMAGE] 请求内容: {}", serde_json::to_string_pretty(&antigravity_request).unwrap_or_default() @@ -312,7 +312,7 @@ pub async fn handle_image_generation( .logs .write() .await - .add("error", &format!("[IMAGE] 响应转换失败: {}", e)); + .add("error", &format!("[IMAGE] 响应转换失败: {e}")); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ @@ -335,7 +335,7 @@ pub async fn handle_image_generation( .logs .write() .await - .add("error", &format!("[IMAGE] Antigravity API 调用失败: {}", e)); + .add("error", &format!("[IMAGE] Antigravity API 调用失败: {e}")); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ diff --git a/src-tauri/src/server/handlers/kiro_credential.rs b/src-tauri/src/server/handlers/kiro_credential.rs index ca9fe6b0b..ace9d57cc 100644 --- a/src-tauri/src/server/handlers/kiro_credential.rs +++ b/src-tauri/src/server/handlers/kiro_credential.rs @@ -128,14 +128,14 @@ pub async fn get_available_credentials( let credentials = { let conn = db.lock().map_err(|e| ApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; ProviderPoolDao::get_all(&conn) .map_err(|e| ApiError { error: "database_query_error".to_string(), - message: format!("查询凭证失败: {}", e), + message: format!("查询凭证失败: {e}"), status_code: 500, })? .into_iter() @@ -152,12 +152,12 @@ pub async fn get_available_credentials( .get_cache_status(db, &credential.uuid) .map_err(|e| ApiError { error: "cache_query_error".to_string(), - message: format!("获取缓存状态失败: {}", e), + message: format!("获取缓存状态失败: {e}"), status_code: 500, })?; // 计算健康状态分数 - let health_score = calculate_health_score(&credential, cache_status.as_ref()); + let health_score = calculate_health_score(credential, cache_status.as_ref()); let is_available = health_score > 50.0; // 健康分数大于50认为可用 if is_available { @@ -232,19 +232,19 @@ pub async fn select_credential( // 强制选择指定UUID let conn = db.lock().map_err(|e| ApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; - ProviderPoolDao::get_by_uuid(&conn, &force_uuid) + ProviderPoolDao::get_by_uuid(&conn, force_uuid) .map_err(|e| ApiError { error: "database_query_error".to_string(), - message: format!("查询凭证失败: {}", e), + message: format!("查询凭证失败: {e}"), status_code: 500, })? .ok_or_else(|| ApiError { error: "credential_not_found".to_string(), - message: format!("未找到UUID为{}的凭证", force_uuid), + message: format!("未找到UUID为{force_uuid}的凭证"), status_code: 404, })? } else { @@ -254,7 +254,7 @@ pub async fn select_credential( .select_credential(db, "kiro", request.model.as_deref()) .map_err(|e| ApiError { error: "selection_error".to_string(), - message: format!("凭证选择失败: {}", e), + message: format!("凭证选择失败: {e}"), status_code: 500, })? .ok_or_else(|| ApiError { @@ -330,19 +330,19 @@ pub async fn refresh_credential( let credential = { let conn = db.lock().map_err(|e| ApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; let cred = ProviderPoolDao::get_by_uuid(&conn, &uuid) .map_err(|e| ApiError { error: "database_query_error".to_string(), - message: format!("查询凭证失败: {}", e), + message: format!("查询凭证失败: {e}"), status_code: 500, })? .ok_or_else(|| ApiError { error: "credential_not_found".to_string(), - message: format!("未找到UUID为{}的凭证", uuid), + message: format!("未找到UUID为{uuid}的凭证"), status_code: 404, })?; @@ -368,7 +368,7 @@ pub async fn refresh_credential( .get_cache_status(db, &uuid) .map_err(|e| ApiError { error: "cache_query_error".to_string(), - message: format!("获取刷新后缓存状态失败: {}", e), + message: format!("获取刷新后缓存状态失败: {e}"), status_code: 500, })?; @@ -445,19 +445,19 @@ pub async fn get_credential_status( let credential = { let conn = db.lock().map_err(|e| ApiError { error: "database_lock_error".to_string(), - message: format!("数据库锁定失败: {}", e), + message: format!("数据库锁定失败: {e}"), status_code: 500, })?; ProviderPoolDao::get_by_uuid(&conn, &uuid) .map_err(|e| ApiError { error: "database_query_error".to_string(), - message: format!("查询凭证失败: {}", e), + message: format!("查询凭证失败: {e}"), status_code: 500, })? .ok_or_else(|| ApiError { error: "credential_not_found".to_string(), - message: format!("未找到UUID为{}的凭证", uuid), + message: format!("未找到UUID为{uuid}的凭证"), status_code: 404, })? }; @@ -467,7 +467,7 @@ pub async fn get_credential_status( .get_cache_status(db, &uuid) .map_err(|e| ApiError { error: "cache_query_error".to_string(), - message: format!("获取缓存状态失败: {}", e), + message: format!("获取缓存状态失败: {e}"), status_code: 500, })?; @@ -541,7 +541,7 @@ pub async fn get_credential_status( "last_refresh_error".to_string(), cache .last_refresh_error - .map(|err| serde_json::Value::String(err)) + .map(serde_json::Value::String) .unwrap_or(serde_json::Value::Null), ); } else { diff --git a/src-tauri/src/server/handlers/management.rs b/src-tauri/src/server/handlers/management.rs index 1c983c03e..7ca9f66da 100644 --- a/src-tauri/src/server/handlers/management.rs +++ b/src-tauri/src/server/handlers/management.rs @@ -480,7 +480,7 @@ pub async fn management_add_credential( StatusCode::INTERNAL_SERVER_ERROR, Json(AddCredentialResponse { success: false, - message: format!("Failed to add credential: {}", e), + message: format!("Failed to add credential: {e}"), id: None, }), ); @@ -548,7 +548,7 @@ pub async fn management_update_config( StatusCode::BAD_REQUEST, Json(UpdateConfigResponse { success: false, - message: format!("Invalid provider type: {}", provider), + message: format!("Invalid provider type: {provider}"), }), ); } diff --git a/src-tauri/src/server/handlers/provider_calls.rs b/src-tauri/src/server/handlers/provider_calls.rs index 9d718acdf..89a6c607f 100644 --- a/src-tauri/src/server/handlers/provider_calls.rs +++ b/src-tauri/src/server/handlers/provider_calls.rs @@ -138,7 +138,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); return ( StatusCode::INTERNAL_SERVER_ERROR, @@ -151,7 +151,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Token refresh failed: {}", e)), + Some(&format!("Token refresh failed: {e}")), ); return ( StatusCode::UNAUTHORIZED, @@ -233,7 +233,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Token refresh failed: {}", e)), + Some(&format!("Token refresh failed: {e}")), ); return ( StatusCode::UNAUTHORIZED, @@ -279,7 +279,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Retry failed: {}", body)), + Some(&format!("Retry failed: {body}")), ); ( StatusCode::INTERNAL_SERVER_ERROR, @@ -341,7 +341,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); } return ( @@ -693,7 +693,7 @@ pub async fn call_provider_anthropic( Err(e) => { state.logs.write().await.add( "error", - &format!("[CLAUDE] 读取响应失败: {}", e), + &format!("[CLAUDE] 读取响应失败: {e}"), ); if let Some(db) = &state.db { let _ = state.pool_service.mark_unhealthy( @@ -882,7 +882,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("API error: {}", status)), + Some(&format!("API error: {status}")), ); } ( @@ -905,7 +905,7 @@ pub async fn call_provider_anthropic( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("API call failed: {}", e)), + Some(&format!("API call failed: {e}")), ); } ( @@ -982,7 +982,7 @@ pub async fn call_provider_openai( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); return ( StatusCode::INTERNAL_SERVER_ERROR, @@ -994,7 +994,7 @@ pub async fn call_provider_openai( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Token refresh failed: {}", e)), + Some(&format!("Token refresh failed: {e}")), ); return ( StatusCode::UNAUTHORIZED, @@ -1231,20 +1231,20 @@ pub async fn call_provider_openai( } CredentialData::AntigravityOAuth { creds_file_path, project_id } => { eprintln!("\n========== [ANTIGRAVITY] 开始处理 Antigravity 请求 =========="); - eprintln!("[ANTIGRAVITY] 凭证文件: {}", creds_file_path); - eprintln!("[ANTIGRAVITY] 项目ID: {:?}", project_id); + eprintln!("[ANTIGRAVITY] 凭证文件: {creds_file_path}"); + eprintln!("[ANTIGRAVITY] 项目ID: {project_id:?}"); eprintln!("[ANTIGRAVITY] 模型: {}", request.model); eprintln!("[ANTIGRAVITY] 流式: {}", request.stream); let mut antigravity = AntigravityProvider::new(); if let Err(e) = antigravity.load_credentials_from_path(creds_file_path).await { - eprintln!("[ANTIGRAVITY] 加载凭证失败: {}", e); + eprintln!("[ANTIGRAVITY] 加载凭证失败: {e}"); // 记录凭证加载失败 if let Some(db) = &state.db { let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); } return ( @@ -1257,7 +1257,7 @@ pub async fn call_provider_openai( // 使用新的 validate_token() 方法检查 Token 状态 let validation_result = antigravity.validate_token(); - eprintln!("[ANTIGRAVITY] Token 验证结果: {:?}", validation_result); + eprintln!("[ANTIGRAVITY] Token 验证结果: {validation_result:?}"); eprintln!("[ANTIGRAVITY] needs_refresh() = {}", validation_result.needs_refresh()); tracing::info!("[Antigravity] Token 验证结果: {:?}", validation_result); @@ -1279,7 +1279,7 @@ pub async fn call_provider_openai( } } Err(refresh_error) => { - eprintln!("[ANTIGRAVITY] Token 刷新失败: {:?}", refresh_error); + eprintln!("[ANTIGRAVITY] Token 刷新失败: {refresh_error:?}"); tracing::error!("[Antigravity] Token 刷新失败: {:?}", refresh_error); // 使用新的 mark_unhealthy_with_details 方法 if let Some(db) = &state.db { @@ -1403,7 +1403,7 @@ pub async fn call_provider_openai( "finish_reason": null }] }); - sse_events.push_str(&format!("data: {}\n\n", chunk_response.to_string())); + sse_events.push_str(&format!("data: {chunk_response}\n\n")); } // 发送结束 chunk @@ -1418,7 +1418,7 @@ pub async fn call_provider_openai( "finish_reason": "stop" }] }); - sse_events.push_str(&format!("data: {}\n\n", done_response.to_string())); + sse_events.push_str(&format!("data: {done_response}\n\n")); sse_events.push_str("data: [DONE]\n\n"); return Response::builder() @@ -1476,7 +1476,7 @@ pub async fn call_provider_openai( } } Err(e) => { - eprintln!("[ANTIGRAVITY_STREAM] chunk #{} 错误: {}", chunk_count, e); + eprintln!("[ANTIGRAVITY_STREAM] chunk #{chunk_count} 错误: {e}"); let _ = tx.send(Err(e.to_string())); return; } @@ -1499,7 +1499,7 @@ pub async fn call_provider_openai( yield Ok::<_, std::io::Error>(axum::body::Bytes::from(sse_content)); } Ok(Err(e)) => { - eprintln!("[ANTIGRAVITY_STREAM] 解析错误: {}", e); + eprintln!("[ANTIGRAVITY_STREAM] 解析错误: {e}"); let error_event = format!( "data: {{\"error\": {{\"message\": \"{}\"}}}}\n\ndata: [DONE]\n\n", e.replace("\"", "\\\"") @@ -1544,7 +1544,7 @@ pub async fn call_provider_openai( // 获取 project_id 用于请求 let proj_id = antigravity.project_id.clone().unwrap_or_default(); - eprintln!("[ANTIGRAVITY_OPENAI] 项目ID: {}", proj_id); + eprintln!("[ANTIGRAVITY_OPENAI] 项目ID: {proj_id}"); // 转换请求格式 eprintln!("[ANTIGRAVITY_OPENAI] 开始转换请求格式..."); @@ -1868,14 +1868,12 @@ pub async fn call_provider_openai( ); let _ = state.pool_service.record_usage(db, &credential.uuid); } - } else { - if let Some(db) = &state.db { - let _ = state.pool_service.mark_unhealthy( - db, - &credential.uuid, - Some(&format!("API error: {}", status)), - ); - } + } else if let Some(db) = &state.db { + let _ = state.pool_service.mark_unhealthy( + db, + &credential.uuid, + Some(&format!("API error: {status}")), + ); } match resp.bytes().await { @@ -1902,7 +1900,7 @@ pub async fn call_provider_openai( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("API call failed: {}", e)), + Some(&format!("API call failed: {e}")), ); } ( @@ -2012,7 +2010,7 @@ pub async fn call_provider_openai( &json, &mut state.convert_state, ) { - output.push_str(&format!("data: {}\n\n", converted)); + output.push_str(&format!("data: {converted}\n\n")); } } } @@ -2022,7 +2020,7 @@ pub async fn call_provider_openai( } Err(e) => { tracing::error!("[Codex] Stream error: {}", e); - Err(std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + Err(std::io::Error::other(e.to_string())) } } } @@ -2714,7 +2712,7 @@ pub async fn handle_kiro_stream( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); return ( StatusCode::INTERNAL_SERVER_ERROR, @@ -2726,7 +2724,7 @@ pub async fn handle_kiro_stream( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Token refresh failed: {}", e)), + Some(&format!("Token refresh failed: {e}")), ); return ( StatusCode::UNAUTHORIZED, @@ -2781,7 +2779,7 @@ pub async fn handle_kiro_stream( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Token refresh failed: {}", refresh_err)), + Some(&format!("Token refresh failed: {refresh_err}")), ); return ( StatusCode::UNAUTHORIZED, @@ -2951,7 +2949,7 @@ pub async fn handle_kiro_stream( if let Some(ref fid) = flow_id_for_stream { let flow_error = FlowError::new( flow_error_type, - format!("流式传输错误: {}", e), + format!("流式传输错误: {e}"), ); flow_monitor_for_stream.fail_flow(fid, flow_error).await; } @@ -3067,7 +3065,7 @@ fn parse_antigravity_accumulated_response(data: &str, model: &str) -> Result Result(&data[json_start..]) { - eprintln!("[ANTIGRAVITY_PARSE] 在位置 {} 找到有效 JSON", json_start); + eprintln!("[ANTIGRAVITY_PARSE] 在位置 {json_start} 找到有效 JSON"); return parse_antigravity_json(&json, model); } start = json_start + 1; @@ -3135,7 +3133,7 @@ fn parse_antigravity_accumulated_response(data: &str, model: &str) -> Result .unwrap_or("image/png"); // 将图片作为 markdown 格式的 data URL - let image_url = format!("data:{};base64,{}", mime_type, data); - image_data = Some(format!("\n\n![Generated Image]({})", image_url)); + let image_url = format!("data:{mime_type};base64,{data}"); + image_data = Some(format!("\n\n![Generated Image]({image_url})")); has_image = true; } } @@ -3410,7 +3408,7 @@ fn convert_gemini_chunk_to_openai_sse(json: &serde_json::Value, model: &str) -> // 合并文本和图片内容 let final_content = match (content_delta, image_data) { - (Some(text), Some(img)) => Some(format!("{}{}", text, img)), + (Some(text), Some(img)) => Some(format!("{text}{img}")), (Some(text), None) => Some(text), (None, Some(img)) => Some(img), (None, None) => None, @@ -3438,7 +3436,7 @@ fn convert_gemini_chunk_to_openai_sse(json: &serde_json::Value, model: &str) -> }] }); - Some(format!("data: {}\n\n", response.to_string())) + Some(format!("data: {response}\n\n")) } /// 将 OpenAI ChatCompletionResponse 转换为 Anthropic MessagesResponse 格式 diff --git a/src-tauri/src/server/handlers/websocket.rs b/src-tauri/src/server/handlers/websocket.rs index 50cd21250..338c758ad 100644 --- a/src-tauri/src/server/handlers/websocket.rs +++ b/src-tauri/src/server/handlers/websocket.rs @@ -152,11 +152,7 @@ pub async fn handle_websocket( if let Ok(msg_text) = serde_json::to_string(&ws_msg) { let mut sender_guard = flow_sender.lock().await; - if sender_guard - .send(WsMessage::Text(msg_text.into())) - .await - .is_err() - { + if sender_guard.send(WsMessage::Text(msg_text)).await.is_err() { tracing::debug!( "[WS] Flow event send failed for connection {}", &conn_id_clone[..8] @@ -197,11 +193,7 @@ pub async fn handle_websocket( if let Some(resp) = response { let resp_text = serde_json::to_string(&resp).unwrap_or_default(); let mut sender_guard = sender.lock().await; - if sender_guard - .send(WsMessage::Text(resp_text.into())) - .await - .is_err() - { + if sender_guard.send(WsMessage::Text(resp_text)).await.is_err() { break; } } @@ -209,13 +201,12 @@ pub async fn handle_websocket( Err(e) => { state.ws_manager.on_error(); let error = WsProtoMessage::Error(WsError::invalid_message(format!( - "Failed to parse message: {}", - e + "Failed to parse message: {e}" ))); let error_text = serde_json::to_string(&error).unwrap_or_default(); let mut sender_guard = sender.lock().await; if sender_guard - .send(WsMessage::Text(error_text.into())) + .send(WsMessage::Text(error_text)) .await .is_err() { @@ -232,7 +223,7 @@ pub async fn handle_websocket( let error_text = serde_json::to_string(&error).unwrap_or_default(); let mut sender_guard = sender.lock().await; if sender_guard - .send(WsMessage::Text(error_text.into())) + .send(WsMessage::Text(error_text)) .await .is_err() { @@ -407,7 +398,7 @@ async fn handle_ws_api_request(state: &AppState, request: &WsApiRequest) -> WsPr } Err(e) => WsProtoMessage::Error(WsError::invalid_request( Some(request.request_id.clone()), - format!("Invalid chat completion request: {}", e), + format!("Invalid chat completion request: {e}"), )), } } @@ -419,7 +410,7 @@ async fn handle_ws_api_request(state: &AppState, request: &WsApiRequest) -> WsPr } Err(e) => WsProtoMessage::Error(WsError::invalid_request( Some(request.request_id.clone()), - format!("Invalid messages request: {}", e), + format!("Invalid messages request: {e}"), )), } } @@ -485,8 +476,7 @@ async fn handle_ws_chat_completions( WsProtoMessage::Error(WsError::internal( Some(request_id.to_string()), format!( - "No available credentials for provider '{}'. Please add credentials in the Provider Pool.", - default_provider + "No available credentials for provider '{default_provider}'. Please add credentials in the Provider Pool." ), )) } @@ -549,8 +539,7 @@ async fn handle_ws_anthropic_messages( WsProtoMessage::Error(WsError::internal( Some(request_id.to_string()), format!( - "No available credentials for provider '{}'. Please add credentials in the Provider Pool.", - default_provider + "No available credentials for provider '{default_provider}'. Please add credentials in the Provider Pool." ), )) } @@ -572,7 +561,7 @@ pub async fn call_provider_openai_for_ws( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); } return Err(e.to_string()); @@ -582,7 +571,7 @@ pub async fn call_provider_openai_for_ws( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Token refresh failed: {}", e)), + Some(&format!("Token refresh failed: {e}")), ); } return Err(e.to_string()); @@ -663,7 +652,7 @@ pub async fn call_provider_openai_for_ws( .pool_service .mark_unhealthy(db, &credential.uuid, Some(&body)); } - Err(format!("Upstream error: {}", body)) + Err(format!("Upstream error: {body}")) } } CredentialData::OpenAIKey { api_key, base_url } => { @@ -700,7 +689,7 @@ pub async fn call_provider_openai_for_ws( .pool_service .mark_unhealthy(db, &credential.uuid, Some(&body)); } - Err(format!("Upstream error: {}", body)) + Err(format!("Upstream error: {body}")) } } CredentialData::ClaudeKey { api_key, base_url } => { @@ -750,7 +739,7 @@ pub async fn call_provider_openai_for_ws( let _ = state.pool_service.mark_unhealthy( db, &credential.uuid, - Some(&format!("Failed to load credentials: {}", e)), + Some(&format!("Failed to load credentials: {e}")), ); } return Err(e.to_string()); @@ -885,7 +874,7 @@ pub async fn call_provider_anthropic_for_ws( .pool_service .mark_unhealthy(db, &credential.uuid, Some(&body)); } - Err(format!("Upstream error: {}", body)) + Err(format!("Upstream error: {body}")) } } _ => { diff --git a/src-tauri/src/server/mod.rs b/src-tauri/src/server/mod.rs index 1eb2acf36..f0ab9f4fe 100644 --- a/src-tauri/src/server/mod.rs +++ b/src-tauri/src/server/mod.rs @@ -361,9 +361,8 @@ impl ServerState { default_provider_str ); eprintln!( - "[SERVER] 警告:默认 Provider '{}' 不是标准 Provider 类型(kiro/openai/claude等),\ - 可能是自定义 Provider ID。如果这是预期行为,请忽略此警告。", - default_provider_str + "[SERVER] 警告:默认 Provider '{default_provider_str}' 不是标准 Provider 类型(kiro/openai/claude等),\ + 可能是自定义 Provider ID。如果这是预期行为,请忽略此警告。" ); } } @@ -565,18 +564,15 @@ async fn start_config_watcher( ); logs_clone.write().await.add( "info", - &format!( - "[HOT_RELOAD] 凭证池同步完成,共 {} 个凭证", - count - ), + &format!("[HOT_RELOAD] 凭证池同步完成,共 {count} 个凭证"), ); } Err(e) => { tracing::warn!("[HOT_RELOAD] 凭证池同步失败: {}", e); - logs_clone.write().await.add( - "warn", - &format!("[HOT_RELOAD] 凭证池同步失败: {}", e), - ); + logs_clone + .write() + .await + .add("warn", &format!("[HOT_RELOAD] 凭证池同步失败: {e}")); } } } @@ -585,7 +581,7 @@ async fn start_config_watcher( tracing::warn!("[HOT_RELOAD] 配置热重载失败,已回滚: {}", error); logs_clone.write().await.add( "warn", - &format!("[HOT_RELOAD] 配置热重载失败,已回滚: {}", error), + &format!("[HOT_RELOAD] 配置热重载失败,已回滚: {error}"), ); } ReloadResult::Failed { @@ -601,8 +597,7 @@ async fn start_config_watcher( logs_clone.write().await.add( "error", &format!( - "[HOT_RELOAD] 配置热重载失败: {}, 回滚错误: {:?}", - error, rollback_error + "[HOT_RELOAD] 配置热重载失败: {error}, 回滚错误: {rollback_error:?}" ), ); } @@ -712,7 +707,7 @@ async fn sync_credential_pool_from_config( // 从配置加载凭证 let credentials = sync_service.load_from_config().map_err(|e| e.to_string())?; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut synced_count = 0; for cred in &credentials { @@ -765,7 +760,7 @@ async fn run_server( config_path: Option, processor: Option>, ) -> Result<(), Box> { - let base_url = format!("http://{}:{}", host, port); + let base_url = format!("http://{host}:{port}"); // 使用传入的 processor 或创建新的 let processor = match processor { @@ -810,8 +805,7 @@ async fn run_server( default_provider_str ); eprintln!( - "[SERVER] 警告:默认 Provider '{}' 不是标准 Provider 类型,可能是自定义 Provider ID", - default_provider_str + "[SERVER] 警告:默认 Provider '{default_provider_str}' 不是标准 Provider 类型,可能是自定义 Provider ID" ); } } @@ -912,7 +906,7 @@ async fn run_server( eprintln!("[DevBridge] 启动完成"); } Err(e) => { - eprintln!("[DevBridge] 启动失败: {}", e); + eprintln!("[DevBridge] 启动失败: {e}"); } } }); @@ -1038,13 +1032,10 @@ async fn run_server( let addr: std::net::SocketAddr = format!("{host}:{port}") .parse() - .map_err(|e| format!("无效的监听地址 {}:{} - {}", host, port, e))?; + .map_err(|e| format!("无效的监听地址 {host}:{port} - {e}"))?; let listener = tokio::net::TcpListener::bind(addr).await.map_err(|e| { - format!( - "无法绑定到 {}:{},错误: {}。请检查地址是否有效或端口是否被占用。", - host, port, e - ) + format!("无法绑定到 {host}:{port},错误: {e}。请检查地址是否有效或端口是否被占用。") })?; tracing::info!("Server listening on {}", addr); @@ -1108,10 +1099,7 @@ async fn gemini_generate_content( state.logs.write().await.add( "info", - &format!( - "[GEMINI] POST /v1/gemini/{} model={} method={}", - path, model, method - ), + &format!("[GEMINI] POST /v1/gemini/{path} model={model} method={method}"), ); // 目前只支持 generateContent 方法 @@ -1247,7 +1235,7 @@ async fn gemini_generate_content( let adj = adjectives[(bytes[0] as usize) % adjectives.len()]; let noun = nouns[(bytes[1] as usize) % nouns.len()]; let random_part: String = uuid.to_string()[..5].to_lowercase(); - antigravity.project_id = Some(format!("{}-{}-{}", adj, noun, random_part)); + antigravity.project_id = Some(format!("{adj}-{noun}-{random_part}")); } } @@ -1261,7 +1249,7 @@ async fn gemini_generate_content( .logs .write() .await - .add("debug", &format!("[GEMINI] 使用 project_id: {}", proj_id)); + .add("debug", &format!("[GEMINI] 使用 project_id: {proj_id}")); // 构建 Antigravity 请求体 // 直接使用用户传入的 Gemini 格式请求,只添加必要的字段 @@ -1380,7 +1368,7 @@ async fn gemini_generate_content( let adj = adjectives[(bytes[0] as usize) % adjectives.len()]; let noun = nouns[(bytes[1] as usize) % nouns.len()]; let random_part: String = uuid.to_string()[..5].to_lowercase(); - gemini.project_id = Some(format!("{}-{}-{}", adj, noun, random_part)); + gemini.project_id = Some(format!("{adj}-{noun}-{random_part}")); } } @@ -1389,10 +1377,11 @@ async fn gemini_generate_content( format!("proxycast-{}", &uuid.to_string()[..8]) }); - state.logs.write().await.add( - "debug", - &format!("[GEMINI CLI] 使用 project_id: {}", proj_id), - ); + state + .logs + .write() + .await + .add("debug", &format!("[GEMINI CLI] 使用 project_id: {proj_id}")); // 构建 Gemini CLI 请求体 // Gemini CLI 使用 Cloud Code Assist 端点,不做模型名称映射 @@ -1442,7 +1431,7 @@ async fn gemini_generate_content( .logs .write() .await - .add("error", &format!("[GEMINI CLI] 请求失败: {}", api_err)); + .add("error", &format!("[GEMINI CLI] 请求失败: {api_err}")); build_error_response(&api_err.to_string()) } @@ -1494,7 +1483,7 @@ async fn list_routes(State(state): State) -> impl IntoResponse { .all_ips .iter() .find(|ip| ip.starts_with("192.168.") || ip.starts_with("10.")) - .or_else(|| network_info.lan_ip.as_ref()) + .or(network_info.lan_ip.as_ref()) .or_else(|| network_info.all_ips.first()) .cloned() .unwrap_or_else(|| "localhost".to_string()); @@ -1527,12 +1516,12 @@ async fn list_routes(State(state): State) -> impl IntoResponse { crate::models::route_model::RouteEndpoint { path: "/v1/messages".to_string(), protocol: "claude".to_string(), - url: format!("{}/v1/messages", display_base_url), + url: format!("{display_base_url}/v1/messages"), }, crate::models::route_model::RouteEndpoint { path: "/v1/chat/completions".to_string(), protocol: "openai".to_string(), - url: format!("{}/v1/chat/completions", display_base_url), + url: format!("{display_base_url}/v1/chat/completions"), }, ], tags: vec!["默认".to_string()], @@ -1560,7 +1549,7 @@ async fn anthropic_messages_with_selector( if let Err(e) = handlers::verify_api_key_anthropic(&headers, &state.api_key).await { state.logs.write().await.add( "warn", - &format!("Unauthorized request to /{}/v1/messages", selector), + &format!("Unauthorized request to /{selector}/v1/messages"), ); return e.into_response(); } @@ -1619,8 +1608,7 @@ async fn anthropic_messages_with_selector( state.logs.write().await.add( "error", &format!( - "[ROUTE] No available credentials for selector '{}', refusing to fallback", - selector + "[ROUTE] No available credentials for selector '{selector}', refusing to fallback" ), ); ( @@ -1647,7 +1635,7 @@ async fn chat_completions_with_selector( if let Err(e) = handlers::verify_api_key(&headers, &state.api_key).await { state.logs.write().await.add( "warn", - &format!("Unauthorized request to /{}/v1/chat/completions", selector), + &format!("Unauthorized request to /{selector}/v1/chat/completions"), ); return e.into_response(); } @@ -1700,8 +1688,7 @@ async fn chat_completions_with_selector( state.logs.write().await.add( "error", &format!( - "[ROUTE] No available credentials for selector '{}', refusing to fallback", - selector + "[ROUTE] No available credentials for selector '{selector}', refusing to fallback" ), ); ( diff --git a/src-tauri/src/server_utils.rs b/src-tauri/src/server_utils.rs index a81bdd004..c7c4e2f68 100644 --- a/src-tauri/src/server_utils.rs +++ b/src-tauri/src/server_utils.rs @@ -696,7 +696,7 @@ pub fn build_gemini_native_request( let n: u64 = u64::from_le_bytes([ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], ]) % 9_000_000_000_000_000_000; - format!("-{}", n) + format!("-{n}") }; // 构建内部请求 @@ -869,7 +869,7 @@ mod property_tests { ], ) .prop_map(|(id, name, args)| ToolCall { - id: format!("call_{}", id), + id: format!("call_{id}"), call_type: "function".to_string(), function: FunctionCall { name, @@ -1053,7 +1053,7 @@ mod property_tests { prop_assert_eq!(parts.status, StatusCode::OK); // 即使内容为空,content 数组也应该有一个空文本元素 - let content_array = vec![serde_json::json!({"type": "text", "text": ""})]; + let content_array = [serde_json::json!({"type": "text", "text": ""})]; prop_assert!(!content_array.is_empty()); } @@ -1202,8 +1202,7 @@ mod property_tests { for required in &required_models { assert!( model_ids.contains(required), - "Model {} should be in the list", - required + "Model {required} should be in the list" ); } } @@ -1256,8 +1255,7 @@ mod property_tests { let actual_model = result.get("model").and_then(|v| v.as_str()).unwrap(); assert_eq!( actual_model, *expected, - "Model {} should map to {}", - input, expected + "Model {input} should map to {expected}" ); } @@ -1268,8 +1266,7 @@ mod property_tests { let actual_model = result.get("model").and_then(|v| v.as_str()).unwrap(); assert_eq!( actual_model, *model, - "Unknown model {} should be returned unchanged", - model + "Unknown model {model} should be returned unchanged" ); } } @@ -1316,8 +1313,7 @@ mod property_tests { for model in &thinking_enabled_models { assert!( should_enable_thinking(model), - "Model {} should have thinking enabled", - model + "Model {model} should have thinking enabled" ); } @@ -1333,8 +1329,7 @@ mod property_tests { for model in &thinking_disabled_models { assert!( !should_enable_thinking(model), - "Model {} should have thinking disabled", - model + "Model {model} should have thinking disabled" ); } } @@ -1372,14 +1367,12 @@ mod property_tests { assert_eq!( thinking_config["includeThoughts"].as_bool(), Some(true), - "Model {} should have includeThoughts=true", - model + "Model {model} should have includeThoughts=true" ); assert_eq!( thinking_config["thinkingBudget"].as_i64(), Some(1024), - "Model {} should have thinkingBudget=1024", - model + "Model {model} should have thinkingBudget=1024" ); } @@ -1397,14 +1390,12 @@ mod property_tests { assert_eq!( thinking_config["includeThoughts"].as_bool(), Some(false), - "Model {} should have includeThoughts=false", - model + "Model {model} should have includeThoughts=false" ); assert_eq!( thinking_config["thinkingBudget"].as_i64(), Some(0), - "Model {} should have thinkingBudget=0", - model + "Model {model} should have thinkingBudget=0" ); } } diff --git a/src-tauri/src/services/api_key_provider_service.rs b/src-tauri/src/services/api_key_provider_service.rs index f79a03adc..f02a42ffa 100644 --- a/src-tauri/src/services/api_key_provider_service.rs +++ b/src-tauri/src/services/api_key_provider_service.rs @@ -141,13 +141,13 @@ impl EncryptionService { fn decrypt(&self, ciphertext: &str) -> Result { let encrypted = BASE64 .decode(ciphertext) - .map_err(|e| format!("Base64 解码失败: {}", e))?; + .map_err(|e| format!("Base64 解码失败: {e}"))?; let decrypted: Vec = encrypted .iter() .enumerate() .map(|(i, b)| b ^ self.key[i % self.key.len()]) .collect(); - String::from_utf8(decrypted).map_err(|e| format!("UTF-8 解码失败: {}", e)) + String::from_utf8(decrypted).map_err(|e| format!("UTF-8 解码失败: {e}")) } /// 检查是否为加密后的值(非明文) @@ -198,7 +198,7 @@ impl ApiKeyProviderService { let provider_with_keys = self .get_provider(db, provider_id)? - .ok_or_else(|| format!("Provider not found: {}", provider_id))?; + .ok_or_else(|| format!("Provider not found: {provider_id}"))?; let provider = &provider_with_keys.provider; @@ -274,14 +274,14 @@ impl ApiKeyProviderService { let resp = provider .call_api(&request) .await - .map_err(|e| format!("API 调用失败: {}", e))?; + .map_err(|e| format!("API 调用失败: {e}"))?; let status = resp.status(); let body = resp.text().await.unwrap_or_default(); if status.is_success() { - let parsed: serde_json::Value = serde_json::from_str(&body) - .map_err(|e| format!("解析响应失败: {} - {}", e, body))?; + let parsed: serde_json::Value = + serde_json::from_str(&body).map_err(|e| format!("解析响应失败: {e} - {body}"))?; let content = parsed["choices"] .as_array() @@ -301,13 +301,13 @@ impl ApiKeyProviderService { let resp2 = provider .call_api(&request2) .await - .map_err(|e| format!("API 调用失败: {}", e))?; + .map_err(|e| format!("API 调用失败: {e}"))?; let status2 = resp2.status(); let body2 = resp2.text().await.unwrap_or_default(); if !status2.is_success() { - return Err(format!("API 返回错误: {} - {}", status2, body2)); + return Err(format!("API 返回错误: {status2} - {body2}")); } let content = Self::parse_chat_completions_sse_content(&body2); @@ -321,7 +321,7 @@ impl ApiKeyProviderService { .await; } - Err(format!("API 返回错误: {} - {}", status, body)) + Err(format!("API 返回错误: {status} - {body}")) } fn parse_chat_completions_sse_content(body: &str) -> String { @@ -379,11 +379,11 @@ impl ApiKeyProviderService { // 构建 /responses 端点 URL let base = api_host.trim_end_matches('/'); let url = if base.ends_with("/v1") { - format!("{}/responses", base) + format!("{base}/responses") } else if base.ends_with("/openai") { - format!("{}/v1/responses", base) + format!("{base}/v1/responses") } else { - format!("{}/v1/responses", base) + format!("{base}/v1/responses") }; // Codex Responses 格式请求体(input 必须是列表) @@ -392,18 +392,18 @@ impl ApiKeyProviderService { let client = reqwest::Client::new(); let resp = client .post(&url) - .header("Authorization", format!("Bearer {}", api_key)) + .header("Authorization", format!("Bearer {api_key}")) .header("Content-Type", "application/json") .json(&request_body) .send() .await - .map_err(|e| format!("API 调用失败: {}", e))?; + .map_err(|e| format!("API 调用失败: {e}"))?; let status = resp.status(); let body = resp.text().await.unwrap_or_default(); if !status.is_success() { - return Err(format!("API 返回错误: {} - {}", status, body)); + return Err(format!("API 返回错误: {status} - {body}")); } // 解析 Codex SSE 响应 @@ -457,7 +457,7 @@ impl ApiKeyProviderService { /// 检查数据库中是否存在系统 Provider,如果不存在则插入 /// **Validates: Requirements 9.3** pub fn initialize_system_providers(&self, db: &DbConnection) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let system_providers = get_system_providers(); let mut inserted_count = 0; @@ -487,7 +487,7 @@ impl ApiKeyProviderService { // 首先确保系统 Provider 已初始化 self.initialize_system_providers(db)?; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let providers = ApiKeyProviderDao::get_all_providers_with_keys(&conn).map_err(|e| e.to_string())?; @@ -514,7 +514,7 @@ impl ApiKeyProviderService { db: &DbConnection, id: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let provider = ApiKeyProviderDao::get_provider_by_id(&conn, id).map_err(|e| e.to_string())?; @@ -564,7 +564,7 @@ impl ApiKeyProviderService { updated_at: now, }; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ApiKeyProviderDao::insert_provider(&conn, &provider).map_err(|e| e.to_string())?; Ok(provider) @@ -586,10 +586,10 @@ impl ApiKeyProviderService { region: Option, custom_models: Option>, ) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut provider = ApiKeyProviderDao::get_provider_by_id(&conn, id) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Provider not found: {}", id))?; + .ok_or_else(|| format!("Provider not found: {id}"))?; // 更新字段 if let Some(n) = name { @@ -636,12 +636,12 @@ impl ApiKeyProviderService { /// 删除自定义 Provider /// 系统 Provider 不允许删除 pub fn delete_custom_provider(&self, db: &DbConnection, id: &str) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 检查是否为系统 Provider let provider = ApiKeyProviderDao::get_provider_by_id(&conn, id) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Provider not found: {}", id))?; + .ok_or_else(|| format!("Provider not found: {id}"))?; if provider.is_system { return Err("不允许删除系统 Provider".to_string()); @@ -668,17 +668,17 @@ impl ApiKeyProviderService { provider_id ); - let mut conn = db.lock().map_err(|e| e.to_string())?; + let mut conn = crate::database::lock_db(db)?; // 使用事务确保操作的原子性 let tx = conn .transaction() - .map_err(|e| format!("开始事务失败: {}", e))?; + .map_err(|e| format!("开始事务失败: {e}"))?; // 验证 Provider 存在 let provider = ApiKeyProviderDao::get_provider_by_id(&tx, provider_id) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Provider not found: {}", provider_id))?; + .ok_or_else(|| format!("Provider not found: {provider_id}"))?; tracing::info!( "[ApiKeyProviderService] 找到 Provider: name={}, id={}", @@ -741,7 +741,7 @@ impl ApiKeyProviderService { } // 提交事务 - tx.commit().map_err(|e| format!("提交事务失败: {}", e))?; + tx.commit().map_err(|e| format!("提交事务失败: {e}"))?; tracing::info!( "[ApiKeyProviderService] 成功添加 API Key: provider={}, alias={:?}", @@ -754,7 +754,7 @@ impl ApiKeyProviderService { /// 删除 API Key pub fn delete_api_key(&self, db: &DbConnection, key_id: &str) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ApiKeyProviderDao::delete_api_key(&conn, key_id).map_err(|e| e.to_string()) } @@ -765,10 +765,10 @@ impl ApiKeyProviderService { key_id: &str, enabled: bool, ) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut key = ApiKeyProviderDao::get_api_key_by_id(&conn, key_id) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("API Key not found: {}", key_id))?; + .ok_or_else(|| format!("API Key not found: {key_id}"))?; key.enabled = enabled; ApiKeyProviderDao::update_api_key(&conn, &key).map_err(|e| e.to_string())?; @@ -783,10 +783,10 @@ impl ApiKeyProviderService { key_id: &str, alias: Option, ) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut key = ApiKeyProviderDao::get_api_key_by_id(&conn, key_id) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("API Key not found: {}", key_id))?; + .ok_or_else(|| format!("API Key not found: {key_id}"))?; key.alias = alias; ApiKeyProviderDao::update_api_key(&conn, &key).map_err(|e| e.to_string())?; @@ -803,7 +803,7 @@ impl ApiKeyProviderService { db: &DbConnection, provider_id: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 获取所有启用的 API Keys let keys = ApiKeyProviderDao::get_enabled_api_keys_by_provider(&conn, provider_id) @@ -836,7 +836,7 @@ impl ApiKeyProviderService { db: &DbConnection, provider_id: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 获取所有启用的 API Keys let keys = ApiKeyProviderDao::get_enabled_api_keys_by_provider(&conn, provider_id) @@ -865,10 +865,10 @@ impl ApiKeyProviderService { /// 记录 API Key 使用 pub fn record_usage(&self, db: &DbConnection, key_id: &str) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let key = ApiKeyProviderDao::get_api_key_by_id(&conn, key_id) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("API Key not found: {}", key_id))?; + .ok_or_else(|| format!("API Key not found: {key_id}"))?; ApiKeyProviderDao::update_api_key_usage(&conn, key_id, key.usage_count + 1, Utc::now()) .map_err(|e| e.to_string()) @@ -881,7 +881,7 @@ impl ApiKeyProviderService { db: &DbConnection, provider_id: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 获取 Provider 信息 let provider = match ApiKeyProviderDao::get_provider_by_id(&conn, provider_id) @@ -928,7 +928,7 @@ impl ApiKeyProviderService { db: &DbConnection, provider_type: ApiProviderType, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 获取所有启用的 API Keys(按类型) let keys = ApiKeyProviderDao::get_enabled_api_keys_by_type(&conn, provider_type) @@ -939,7 +939,7 @@ impl ApiKeyProviderService { } // 使用类型名称作为轮询索引的 key - let type_key = format!("type:{}", provider_type); + let type_key = format!("type:{provider_type}"); let index = { let mut indices = self.round_robin_index.write().map_err(|e| e.to_string())?; indices @@ -958,7 +958,7 @@ impl ApiKeyProviderService { /// 记录 API Key 错误 pub fn record_error(&self, db: &DbConnection, key_id: &str) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ApiKeyProviderDao::increment_api_key_error(&conn, key_id).map_err(|e| e.to_string()) } @@ -983,13 +983,13 @@ impl ApiKeyProviderService { /// 获取 UI 状态 pub fn get_ui_state(&self, db: &DbConnection, key: &str) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ApiKeyProviderDao::get_ui_state(&conn, key).map_err(|e| e.to_string()) } /// 设置 UI 状态 pub fn set_ui_state(&self, db: &DbConnection, key: &str, value: &str) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ApiKeyProviderDao::set_ui_state(&conn, key, value).map_err(|e| e.to_string()) } @@ -1000,7 +1000,7 @@ impl ApiKeyProviderService { db: &DbConnection, sort_orders: Vec<(String, i32)>, ) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ApiKeyProviderDao::update_provider_sort_orders(&conn, &sort_orders) .map_err(|e| e.to_string()) } @@ -1013,7 +1013,7 @@ impl ApiKeyProviderService { db: &DbConnection, include_keys: bool, ) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let providers = ApiKeyProviderDao::get_all_providers_with_keys(&conn).map_err(|e| e.to_string())?; @@ -1067,13 +1067,13 @@ impl ApiKeyProviderService { config_json: &str, ) -> Result { let config: serde_json::Value = - serde_json::from_str(config_json).map_err(|e| format!("JSON 解析失败: {}", e))?; + serde_json::from_str(config_json).map_err(|e| format!("JSON 解析失败: {e}"))?; let providers = config["providers"] .as_array() .ok_or_else(|| "配置格式错误: 缺少 providers 数组".to_string())?; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut imported_providers = 0; let mut skipped_providers = 0; let mut errors = Vec::new(); @@ -1100,11 +1100,11 @@ impl ApiKeyProviderService { // 解析 Provider let provider: ApiKeyProvider = serde_json::from_value(provider_data.clone()) - .map_err(|e| format!("Provider 解析失败: {}", e))?; + .map_err(|e| format!("Provider 解析失败: {e}"))?; // 插入 Provider if let Err(e) = ApiKeyProviderDao::insert_provider(&conn, &provider) { - errors.push(format!("导入 Provider {} 失败: {}", id, e)); + errors.push(format!("导入 Provider {id} 失败: {e}")); continue; } @@ -1148,17 +1148,13 @@ impl ApiKeyProviderService { client_type: Option<&crate::server::client_detector::ClientType>, ) -> Result, String> { eprintln!( - "[get_fallback_credential] 开始查找: pool_type={:?}, provider_id_hint={:?}", - pool_type, provider_id_hint + "[get_fallback_credential] 开始查找: pool_type={pool_type:?}, provider_id_hint={provider_id_hint:?}" ); // 策略 1: 优先通过 provider_id 直接查找 (支持 deepseek, moonshot 等 60+ Provider) // 这些 Provider 在 API Key Provider 中有独立配置,应该优先使用 if let Some(provider_id) = provider_id_hint { - eprintln!( - "[get_fallback_credential] 尝试按 provider_id '{}' 查找", - provider_id - ); + eprintln!("[get_fallback_credential] 尝试按 provider_id '{provider_id}' 查找"); if let Some(cred) = self .find_by_provider_id(db, provider_id, client_type) .await? @@ -1169,18 +1165,12 @@ impl ApiKeyProviderService { ); return Ok(Some(cred)); } - eprintln!( - "[get_fallback_credential] provider_id '{}' 未找到凭证", - provider_id - ); + eprintln!("[get_fallback_credential] provider_id '{provider_id}' 未找到凭证"); } // 策略 2: 通过类型映射查找(降级方案) if let Some(api_type) = self.map_pool_type_to_api_type(pool_type) { - eprintln!( - "[get_fallback_credential] 尝试类型映射: {:?} -> {:?}", - pool_type, api_type - ); + eprintln!("[get_fallback_credential] 尝试类型映射: {pool_type:?} -> {api_type:?}"); if let Some(cred) = self.find_by_api_type(db, pool_type, &api_type)? { eprintln!( "[get_fallback_credential] 通过类型映射找到凭证: {:?}", @@ -1191,8 +1181,7 @@ impl ApiKeyProviderService { } eprintln!( - "[get_fallback_credential] 未找到 {:?} 的降级凭证 (provider_id_hint: {:?})", - pool_type, provider_id_hint + "[get_fallback_credential] 未找到 {pool_type:?} 的降级凭证 (provider_id_hint: {provider_id_hint:?})" ); Ok(None) } @@ -1233,7 +1222,7 @@ impl ApiKeyProviderService { pool_type: &PoolProviderType, api_type: &ApiProviderType, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 查找该类型的启用的 Provider(按 sort_order 排序) let providers = ApiKeyProviderDao::get_all_providers(&conn).map_err(|e| e.to_string())?; @@ -1303,7 +1292,7 @@ impl ApiKeyProviderService { ) -> Result, String> { // First, get all data we need while holding the lock let (provider, keys) = { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 直接按 provider_id 查找 let provider = ApiKeyProviderDao::get_provider_by_id(&conn, provider_id) @@ -1318,14 +1307,11 @@ impl ApiKeyProviderService { p } Some(_p) => { - eprintln!( - "[find_by_provider_id] provider '{}' 存在但未启用", - provider_id - ); + eprintln!("[find_by_provider_id] provider '{provider_id}' 存在但未启用"); return Ok(None); } None => { - eprintln!("[find_by_provider_id] provider '{}' 不存在", provider_id); + eprintln!("[find_by_provider_id] provider '{provider_id}' 不存在"); return Ok(None); } }; @@ -1335,10 +1321,7 @@ impl ApiKeyProviderService { .map_err(|e| e.to_string())?; if keys.is_empty() { - eprintln!( - "[find_by_provider_id] provider '{}' 没有启用的 API Key", - provider_id - ); + eprintln!("[find_by_provider_id] provider '{provider_id}' 没有启用的 API Key"); return Ok(None); } @@ -1409,8 +1392,7 @@ impl ApiKeyProviderService { Some(key) => key, None => { eprintln!( - "[find_by_provider_id] provider '{}' 的所有 API Key 都不兼容当前客户端 ({:?})", - provider_id, client_type + "[find_by_provider_id] provider '{provider_id}' 的所有 API Key 都不兼容当前客户端 ({client_type:?})" ); return Ok(None); } @@ -1479,7 +1461,7 @@ impl ApiKeyProviderService { let now = chrono::Utc::now(); Ok(ProviderCredential { - uuid: format!("fallback-{}", key_id), + uuid: format!("fallback-{key_id}"), provider_type: pool_type, credential: credential_data, name: Some(format!("[降级] {}", provider.name)), @@ -1537,7 +1519,7 @@ impl ApiKeyProviderService { let now = chrono::Utc::now(); Ok(ProviderCredential { - uuid: format!("fallback-{}", key_id), + uuid: format!("fallback-{key_id}"), provider_type: *pool_type, credential: credential_data, name: Some(format!("[降级] {}", provider.name)), @@ -1588,7 +1570,7 @@ impl ApiKeyProviderService { // 获取 Provider 信息 let provider_with_keys = self .get_provider(db, provider_id)? - .ok_or_else(|| format!("Provider not found: {}", provider_id))?; + .ok_or_else(|| format!("Provider not found: {provider_id}"))?; let provider = &provider_with_keys.provider; @@ -1639,7 +1621,7 @@ impl ApiKeyProviderService { } _ => { // OpenAI 兼容类型,优先使用 /models 端点 - eprintln!("[TEST_CONNECTION] model_name param: {:?}", model_name); + eprintln!("[TEST_CONNECTION] model_name param: {model_name:?}"); eprintln!( "[TEST_CONNECTION] provider.custom_models: {:?}", provider.custom_models @@ -1649,7 +1631,7 @@ impl ApiKeyProviderService { .test_openai_models_endpoint(&api_key, &provider.api_host) .await; - eprintln!("[TEST_CONNECTION] models_result: {:?}", models_result); + eprintln!("[TEST_CONNECTION] models_result: {models_result:?}"); // 如果 /models 端点失败: // 1) 优先用传入的 model_name @@ -1657,16 +1639,13 @@ impl ApiKeyProviderService { if models_result.is_err() { let test_model = model_name.or_else(|| provider.custom_models.first().cloned()); - eprintln!("[TEST_CONNECTION] fallback test_model: {:?}", test_model); + eprintln!("[TEST_CONNECTION] fallback test_model: {test_model:?}"); if let Some(test_model) = test_model { let chat_result = self .test_openai_chat_completion(&api_key, &provider.api_host, &test_model) .await; - eprintln!( - "[TEST_CONNECTION] chat_completion result: {:?}", - chat_result - ); + eprintln!("[TEST_CONNECTION] chat_completion result: {chat_result:?}"); chat_result } else { models_result @@ -1709,7 +1688,7 @@ impl ApiKeyProviderService { let response = provider .list_models() .await - .map_err(|e| format!("获取模型列表失败: {}", e))?; + .map_err(|e| format!("获取模型列表失败: {e}"))?; // 解析模型列表 let models: Vec = response["data"] @@ -1761,7 +1740,7 @@ impl ApiKeyProviderService { let response = provider .messages(&request) .await - .map_err(|e| format!("API 调用失败: {}", e))?; + .map_err(|e| format!("API 调用失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1800,7 +1779,7 @@ impl ApiKeyProviderService { let response = provider .messages(&request) .await - .map_err(|e| format!("API 调用失败: {}", e))?; + .map_err(|e| format!("API 调用失败: {e}"))?; if response.status().is_success() { Ok(vec![model.to_string()]) @@ -1813,7 +1792,7 @@ impl ApiKeyProviderService { return Err("CLAUDE_CODE_ONLY".to_string()); } - Err(format!("API 返回错误: {} - {}", status, body)) + Err(format!("API 返回错误: {status} - {body}")) } } @@ -1830,28 +1809,28 @@ impl ApiKeyProviderService { .connect_timeout(Duration::from_secs(10)) .timeout(Duration::from_secs(30)) .build() - .map_err(|e| format!("创建 HTTP 客户端失败: {}", e))?; + .map_err(|e| format!("创建 HTTP 客户端失败: {e}"))?; // Gemini API 的模型列表端点 let base = api_host.trim_end_matches('/'); - let url = format!("{}/v1beta/models?key={}", base, api_key); + let url = format!("{base}/v1beta/models?key={api_key}"); let response = client .get(&url) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("API 返回错误: {} - {}", status, body)); + return Err(format!("API 返回错误: {status} - {body}")); } let data: serde_json::Value = response .json() .await - .map_err(|e| format!("解析响应失败: {}", e))?; + .map_err(|e| format!("解析响应失败: {e}"))?; let models: Vec = data["models"] .as_array() diff --git a/src-tauri/src/services/aster_session_store.rs b/src-tauri/src/services/aster_session_store.rs index 4188747f2..511f57b5d 100644 --- a/src-tauri/src/services/aster_session_store.rs +++ b/src-tauri/src/services/aster_session_store.rs @@ -58,10 +58,7 @@ impl SessionStore for ProxyCastSessionStore { let now = Utc::now(); let now_str = now.to_rfc3339(); - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; let type_str = session_type.to_string(); @@ -70,7 +67,7 @@ impl SessionStore for ProxyCastSessionStore { VALUES (?1, ?2, ?3, ?4, ?5, ?6)", rusqlite::params![id, type_str, None::, name, now_str, now_str], ) - .map_err(|e| anyhow!("创建会话失败: {}", e))?; + .map_err(|e| anyhow!("创建会话失败: {e}"))?; Ok(Session { id, @@ -98,17 +95,41 @@ impl SessionStore for ProxyCastSessionStore { } async fn get_session(&self, id: &str, include_messages: bool) -> Result { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + tracing::info!( + "[SessionStore] get_session 被调用: id={}, include_messages={}", + id, + include_messages + ); + + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; + + // 检查 session 是否存在 + let session_exists: bool = conn + .query_row("SELECT 1 FROM agent_sessions WHERE id = ?", [id], |_| { + Ok(true) + }) + .unwrap_or(false); + + tracing::info!("[SessionStore] session_exists={}", session_exists); + + // 如果不存在,自动创建 + if !session_exists { + let now = Utc::now().to_rfc3339(); + conn.execute( + "INSERT INTO agent_sessions (id, model, system_prompt, title, created_at, updated_at) + VALUES (?1, ?2, ?3, ?4, ?5, ?6)", + rusqlite::params![id, "agent:default", None::, "新对话", now, now], + ) + .map_err(|e| anyhow!("自动创建会话失败: {e}"))?; + tracing::info!("[SessionStore] get_session 自动创建会话: {}", id); + } let mut stmt = conn .prepare( "SELECT id, model, system_prompt, title, created_at, updated_at FROM agent_sessions WHERE id = ?", ) - .map_err(|e| anyhow!("准备查询失败: {}", e))?; + .map_err(|e| anyhow!("准备查询失败: {e}"))?; let session_row = stmt .query_row([id], |row| { @@ -121,7 +142,7 @@ impl SessionStore for ProxyCastSessionStore { row.get::<_, String>(5)?, )) }) - .map_err(|e| anyhow!("会话不存在: {}", e))?; + .map_err(|e| anyhow!("会话不存在: {e}"))?; let (id, model, _system_prompt, title, created_at, updated_at) = session_row; @@ -168,10 +189,12 @@ impl SessionStore for ProxyCastSessionStore { } async fn add_message(&self, session_id: &str, message: &Message) -> Result<()> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + tracing::info!( + "[SessionStore] add_message 被调用: session_id={}", + session_id + ); + + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; // 检查会话是否存在,如果不存在则自动创建 let session_exists: bool = conn @@ -196,13 +219,13 @@ impl SessionStore for ProxyCastSessionStore { now ], ) - .map_err(|e| anyhow!("自动创建会话失败: {}", e))?; + .map_err(|e| anyhow!("自动创建会话失败: {e}"))?; tracing::info!("[SessionStore] 自动创建会话: {}", session_id); } let role = Self::message_role_to_string(message); let content_json = serde_json::to_string(&message.content) - .map_err(|e| anyhow!("序列化消息内容失败: {}", e))?; + .map_err(|e| anyhow!("序列化消息内容失败: {e}"))?; let timestamp = Utc::now().to_rfc3339(); // 从 content 中提取 tool_calls(ToolRequest 类型) @@ -238,13 +261,13 @@ impl SessionStore for ProxyCastSessionStore { VALUES (?1, ?2, ?3, ?4, ?5, ?6)", rusqlite::params![session_id, role, content_json, timestamp, tool_calls_json, tool_call_id], ) - .map_err(|e| anyhow!("添加消息失败: {}", e))?; + .map_err(|e| anyhow!("添加消息失败: {e}"))?; conn.execute( "UPDATE agent_sessions SET updated_at = ? WHERE id = ?", rusqlite::params![timestamp, session_id], ) - .map_err(|e| anyhow!("更新会话时间失败: {}", e))?; + .map_err(|e| anyhow!("更新会话时间失败: {e}"))?; Ok(()) } @@ -254,16 +277,13 @@ impl SessionStore for ProxyCastSessionStore { session_id: &str, conversation: &Conversation, ) -> Result<()> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; conn.execute( "DELETE FROM agent_messages WHERE session_id = ?", [session_id], ) - .map_err(|e| anyhow!("删除旧消息失败: {}", e))?; + .map_err(|e| anyhow!("删除旧消息失败: {e}"))?; for message in conversation.messages() { let role = Self::message_role_to_string(message); @@ -313,10 +333,7 @@ impl SessionStore for ProxyCastSessionStore { } async fn list_sessions(&self) -> Result> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; let mut stmt = conn.prepare( "SELECT id, model, system_prompt, title, created_at, updated_at @@ -381,19 +398,13 @@ impl SessionStore for ProxyCastSessionStore { } async fn delete_session(&self, id: &str) -> Result<()> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; conn.execute("DELETE FROM agent_sessions WHERE id = ?", [id])?; Ok(()) } async fn get_insights(&self) -> Result { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; let total_sessions: i64 = conn.query_row("SELECT COUNT(*) FROM agent_sessions", [], |row| row.get(0))?; @@ -406,12 +417,12 @@ impl SessionStore for ProxyCastSessionStore { async fn export_session(&self, id: &str) -> Result { let session = self.get_session(id, true).await?; - serde_json::to_string_pretty(&session).map_err(|e| anyhow!("导出会话失败: {}", e)) + serde_json::to_string_pretty(&session).map_err(|e| anyhow!("导出会话失败: {e}")) } async fn import_session(&self, json: &str) -> Result { let session: Session = - serde_json::from_str(json).map_err(|e| anyhow!("解析会话 JSON 失败: {}", e))?; + serde_json::from_str(json).map_err(|e| anyhow!("解析会话 JSON 失败: {e}"))?; let new_session = self .create_session( @@ -449,13 +460,9 @@ impl SessionStore for ProxyCastSessionStore { } async fn truncate_conversation(&self, session_id: &str, timestamp: i64) -> Result<()> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; - let dt = - chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(|| Utc::now().into()); + let dt = chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(Utc::now); let timestamp_str = dt.to_rfc3339(); conn.execute( @@ -472,10 +479,7 @@ impl SessionStore for ProxyCastSessionStore { name: String, _user_set: bool, ) -> Result<()> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; conn.execute( "UPDATE agent_sessions SET title = ? WHERE id = ?", rusqlite::params![name, session_id], @@ -502,10 +506,7 @@ impl SessionStore for ProxyCastSessionStore { _model_config: Option, ) -> Result<()> { if let Some(provider) = provider_name { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; conn.execute( "UPDATE agent_sessions SET model = ? WHERE id = ?", rusqlite::params![provider, session_id], @@ -531,10 +532,7 @@ impl SessionStore for ProxyCastSessionStore { _before_date: Option>, _exclude_session_id: Option, ) -> Result> { - let conn = self - .db - .lock() - .map_err(|e| anyhow!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| anyhow!("数据库锁定失败: {e}"))?; let limit = limit.unwrap_or(50); let mut stmt = conn.prepare( @@ -546,7 +544,7 @@ impl SessionStore for ProxyCastSessionStore { LIMIT ?", )?; - let pattern = format!("%{}%", query); + let pattern = format!("%{query}%"); let matches: Vec = stmt .query_map(rusqlite::params![pattern, limit as i64], |row| { let session_id: String = row.get(0)?; diff --git a/src-tauri/src/services/backup_service.rs b/src-tauri/src/services/backup_service.rs index 01d3b430d..c94b096bd 100644 --- a/src-tauri/src/services/backup_service.rs +++ b/src-tauri/src/services/backup_service.rs @@ -18,7 +18,7 @@ pub struct BackupService { impl BackupService { pub fn new(backup_dir: PathBuf, retention_days: u32) -> Result { std::fs::create_dir_all(&backup_dir) - .map_err(|e| format!("无法创建备份目录 {:?}: {}", backup_dir, e))?; + .map_err(|e| format!("无法创建备份目录 {backup_dir:?}: {e}"))?; Ok(Self { backup_dir, retention_days, @@ -34,9 +34,9 @@ impl BackupService { pub fn backup_database(&self) -> Result { let db_path = get_db_path()?; let timestamp = Utc::now().format("%Y%m%d_%H%M%S"); - let backup_path = self.backup_dir.join(format!("proxycast_{}.db", timestamp)); + let backup_path = self.backup_dir.join(format!("proxycast_{timestamp}.db")); - std::fs::copy(&db_path, &backup_path).map_err(|e| format!("备份失败: {}", e))?; + std::fs::copy(&db_path, &backup_path).map_err(|e| format!("备份失败: {e}"))?; self.cleanup_old_backups()?; Ok(backup_path) @@ -44,11 +44,11 @@ impl BackupService { pub fn backup_database_with_connection(&self, db: &DbConnection) -> Result { let timestamp = Utc::now().format("%Y%m%d_%H%M%S"); - let backup_path = self.backup_dir.join(format!("proxycast_{}.db", timestamp)); + let backup_path = self.backup_dir.join(format!("proxycast_{timestamp}.db")); let conn = db.lock().map_err(|_| "数据库锁已被占用".to_string())?; let progress: Option = None; conn.backup(DatabaseName::Main, &backup_path, progress) - .map_err(|e| format!("备份失败: {}", e))?; + .map_err(|e| format!("备份失败: {e}"))?; self.cleanup_old_backups()?; Ok(backup_path) @@ -58,11 +58,11 @@ impl BackupService { // P1 安全修复:验证备份路径在白名单目录内 let canonical_backup = backup_path .canonicalize() - .map_err(|e| format!("无法解析备份路径: {}", e))?; + .map_err(|e| format!("无法解析备份路径: {e}"))?; let canonical_backup_dir = self .backup_dir .canonicalize() - .map_err(|e| format!("无法解析备份目录: {}", e))?; + .map_err(|e| format!("无法解析备份目录: {e}"))?; if !canonical_backup.starts_with(&canonical_backup_dir) { return Err("安全限制:只能从备份目录恢复数据库".to_string()); @@ -72,7 +72,7 @@ impl BackupService { return Err("备份文件不存在".to_string()); } let db_path = get_db_path()?; - std::fs::copy(backup_path, db_path).map_err(|e| format!("恢复失败: {}", e))?; + std::fs::copy(backup_path, db_path).map_err(|e| format!("恢复失败: {e}"))?; Ok(()) } @@ -84,11 +84,11 @@ impl BackupService { // P1 安全修复:验证备份路径在白名单目录内 let canonical_backup = backup_path .canonicalize() - .map_err(|e| format!("无法解析备份路径: {}", e))?; + .map_err(|e| format!("无法解析备份路径: {e}"))?; let canonical_backup_dir = self .backup_dir .canonicalize() - .map_err(|e| format!("无法解析备份目录: {}", e))?; + .map_err(|e| format!("无法解析备份目录: {e}"))?; if !canonical_backup.starts_with(&canonical_backup_dir) { return Err("安全限制:只能从备份目录恢复数据库".to_string()); @@ -100,14 +100,14 @@ impl BackupService { let mut conn = db.lock().map_err(|_| "数据库锁已被占用".to_string())?; let progress: Option = None; conn.restore(DatabaseName::Main, backup_path, progress) - .map_err(|e| format!("恢复失败: {}", e))?; + .map_err(|e| format!("恢复失败: {e}"))?; Ok(()) } pub fn list_backups(&self) -> Result, String> { let mut backups = Vec::new(); let entries = - std::fs::read_dir(&self.backup_dir).map_err(|e| format!("无法读取备份目录: {}", e))?; + std::fs::read_dir(&self.backup_dir).map_err(|e| format!("无法读取备份目录: {e}"))?; for entry in entries.flatten() { let path = entry.path(); if path.extension().map(|e| e == "db").unwrap_or(false) { @@ -120,7 +120,7 @@ impl BackupService { pub fn cleanup_old_backups(&self) -> Result<(), String> { let entries = - std::fs::read_dir(&self.backup_dir).map_err(|e| format!("无法读取备份目录: {}", e))?; + std::fs::read_dir(&self.backup_dir).map_err(|e| format!("无法读取备份目录: {e}"))?; let cutoff = Utc::now() - Duration::days(self.retention_days as i64); for entry in entries.flatten() { diff --git a/src-tauri/src/services/context_memory_service.rs b/src-tauri/src/services/context_memory_service.rs index d2a79b907..06b664f4c 100644 --- a/src-tauri/src/services/context_memory_service.rs +++ b/src-tauri/src/services/context_memory_service.rs @@ -122,7 +122,7 @@ impl ContextMemoryService { /// 创建新的上下文记忆服务 pub fn new(config: ContextMemoryConfig) -> Result { // 确保目录存在 - fs::create_dir_all(&config.memory_dir).map_err(|e| format!("创建记忆目录失败: {}", e))?; + fs::create_dir_all(&config.memory_dir).map_err(|e| format!("创建记忆目录失败: {e}"))?; let service = Self { config, @@ -156,7 +156,7 @@ impl ContextMemoryService { pub fn save_memory_entry(&self, entry: &MemoryEntry) -> Result<(), String> { // 确保会话目录存在 let session_dir = self.get_session_memory_dir(&entry.session_id); - fs::create_dir_all(&session_dir).map_err(|e| format!("创建会话目录失败: {}", e))?; + fs::create_dir_all(&session_dir).map_err(|e| format!("创建会话目录失败: {e}"))?; // 更新缓存 { @@ -213,15 +213,15 @@ impl ContextMemoryService { let empty_error_vec = Vec::new(); let error_entries = error_cache.get(session_id).unwrap_or(&empty_error_vec); let json_data = serde_json::to_string_pretty(error_entries) - .map_err(|e| format!("序列化错误日志失败: {}", e))?; + .map_err(|e| format!("序列化错误日志失败: {e}"))?; fs::write(&file_path, json_data) - .map_err(|e| format!("写入错误日志文件失败: {}", e))?; + .map_err(|e| format!("写入错误日志文件失败: {e}"))?; } _ => { // 其他文件保存为 Markdown let markdown_content = self.generate_markdown_content(&filtered_entries, file_type); fs::write(&file_path, markdown_content) - .map_err(|e| format!("写入记忆文件失败: {}", e))?; + .map_err(|e| format!("写入记忆文件失败: {e}"))?; } } @@ -296,7 +296,7 @@ impl ContextMemoryService { let filtered_entries: Vec<_> = entries .iter() - .filter(|e| !e.archived && file_type.map_or(true, |ft| e.file_type == ft)) + .filter(|e| !e.archived && file_type.is_none_or(|ft| e.file_type == ft)) .cloned() .collect(); @@ -469,7 +469,7 @@ impl ContextMemoryService { .unwrap_or(&"无解决方案".to_string()) )); } - context.push_str("\n"); + context.push('\n'); } // 已解决的错误(最近的几个) @@ -497,11 +497,11 @@ impl ContextMemoryService { return Ok(()); } - let entries = fs::read_dir(&self.config.memory_dir) - .map_err(|e| format!("读取记忆目录失败: {}", e))?; + let entries = + fs::read_dir(&self.config.memory_dir).map_err(|e| format!("读取记忆目录失败: {e}"))?; for entry in entries { - let entry = entry.map_err(|e| format!("读取目录条目失败: {}", e))?; + let entry = entry.map_err(|e| format!("读取目录条目失败: {e}"))?; let path = entry.path(); if path.is_dir() { diff --git a/src-tauri/src/services/file_browser_service.rs b/src-tauri/src/services/file_browser_service.rs index 95f690d1c..f0c271875 100644 --- a/src-tauri/src/services/file_browser_service.rs +++ b/src-tauri/src/services/file_browser_service.rs @@ -330,7 +330,7 @@ pub fn list_directory(path: &str) -> DirectoryListing { path: path.to_string(), parent_path: None, entries: vec![], - error: Some(format!("无法解析路径: {}", e)), + error: Some(format!("无法解析路径: {e}")), }; } }; @@ -419,7 +419,7 @@ pub fn list_directory(path: &str) -> DirectoryListing { path: canonical_path.to_string_lossy().to_string(), parent_path, entries: vec![], - error: Some(format!("无法读取目录: {}", e)), + error: Some(format!("无法读取目录: {e}")), }; } }; @@ -451,7 +451,7 @@ pub fn read_file_preview(path: &str, max_size: Option) -> FilePreview { content: None, is_binary: false, size: 0, - error: Some(format!("无法读取文件元信息: {}", e)), + error: Some(format!("无法读取文件元信息: {e}")), }; } }; @@ -503,7 +503,7 @@ pub fn read_file_preview(path: &str, max_size: Option) -> FilePreview { content: None, is_binary: false, size, - error: Some(format!("无法读取文件: {}", e)), + error: Some(format!("无法读取文件: {e}")), }; } }; @@ -553,12 +553,12 @@ pub async fn create_file(path: String) -> Result<(), String> { // 确保父目录存在 if let Some(parent) = path_buf.parent() { if !parent.exists() { - fs::create_dir_all(parent).map_err(|e| format!("无法创建父目录: {}", e))?; + fs::create_dir_all(parent).map_err(|e| format!("无法创建父目录: {e}"))?; } } // 创建空文件 - fs::File::create(&path_buf).map_err(|e| format!("无法创建文件: {}", e))?; + fs::File::create(&path_buf).map_err(|e| format!("无法创建文件: {e}"))?; debug!("创建文件: {}", path); Ok(()) @@ -574,7 +574,7 @@ pub async fn create_directory(path: String) -> Result<(), String> { return Err("目录已存在".to_string()); } - fs::create_dir_all(&path_buf).map_err(|e| format!("无法创建目录: {}", e))?; + fs::create_dir_all(&path_buf).map_err(|e| format!("无法创建目录: {e}"))?; debug!("创建目录: {}", path); Ok(()) @@ -591,14 +591,14 @@ pub async fn delete_file(path: String, recursive: bool) -> Result<(), String> { if path_buf.is_dir() { if recursive { - fs::remove_dir_all(&path_buf).map_err(|e| format!("无法删除目录: {}", e))?; + fs::remove_dir_all(&path_buf).map_err(|e| format!("无法删除目录: {e}"))?; } else { fs::remove_dir(&path_buf) - .map_err(|e| format!("无法删除目录(目录非空,需要递归删除): {}", e))?; + .map_err(|e| format!("无法删除目录(目录非空,需要递归删除): {e}"))?; } debug!("删除目录: {}", path); } else { - fs::remove_file(&path_buf).map_err(|e| format!("无法删除文件: {}", e))?; + fs::remove_file(&path_buf).map_err(|e| format!("无法删除文件: {e}"))?; debug!("删除文件: {}", path); } @@ -619,7 +619,7 @@ pub async fn rename_file(old_path: String, new_path: String) -> Result<(), Strin return Err("目标文件或目录已存在".to_string()); } - fs::rename(&old_path_buf, &new_path_buf).map_err(|e| format!("无法重命名: {}", e))?; + fs::rename(&old_path_buf, &new_path_buf).map_err(|e| format!("无法重命名: {e}"))?; debug!("重命名: {} -> {}", old_path, new_path); Ok(()) @@ -650,7 +650,7 @@ pub async fn reveal_in_finder(path: String) -> Result<(), String> { std::process::Command::new("open") .args(["-R", &path]) .spawn() - .map_err(|e| format!("无法打开 Finder: {}", e))?; + .map_err(|e| format!("无法打开 Finder: {e}"))?; } #[cfg(target_os = "windows")] @@ -688,7 +688,7 @@ pub async fn open_with_default_app(path: String) -> Result<(), String> { std::process::Command::new("open") .arg(&path) .spawn() - .map_err(|e| format!("无法打开文件: {}", e))?; + .map_err(|e| format!("无法打开文件: {e}"))?; } #[cfg(target_os = "windows")] diff --git a/src-tauri/src/services/general_chat/session_service.rs b/src-tauri/src/services/general_chat/session_service.rs index 8ed0bdb85..91d32e6e1 100644 --- a/src-tauri/src/services/general_chat/session_service.rs +++ b/src-tauri/src/services/general_chat/session_service.rs @@ -95,7 +95,7 @@ impl SessionService { first_line.to_string() } else { let truncated: String = first_line.chars().take(max_length - 3).collect(); - format!("{}...", truncated) + format!("{truncated}...") } } diff --git a/src-tauri/src/services/live_sync.rs b/src-tauri/src/services/live_sync.rs index 1c5da08a2..08ecd31cc 100644 --- a/src-tauri/src/services/live_sync.rs +++ b/src-tauri/src/services/live_sync.rs @@ -139,7 +139,7 @@ pub fn write_env_to_shell_config( for (key, value) in env_vars { // 转义值中的特殊字符 let escaped_value = value.replace('\\', "\\\\").replace('"', "\\\""); - new_content.push_str(&format!("export {}=\"{}\"\n", key, escaped_value)); + new_content.push_str(&format!("export {key}=\"{escaped_value}\"\n")); } new_content.push_str(ENV_BLOCK_END); diff --git a/src-tauri/src/services/live_sync_tests.rs b/src-tauri/src/services/live_sync_tests.rs index 0e652aca3..a402ad4a4 100644 --- a/src-tauri/src/services/live_sync_tests.rs +++ b/src-tauri/src/services/live_sync_tests.rs @@ -143,19 +143,18 @@ mod tests { for (i, test_data) in test_cases.iter().enumerate() { // 写入 write_json_file_atomic(&test_file, test_data) - .unwrap_or_else(|e| panic!("Write should succeed for case {}: {}", i, e)); + .unwrap_or_else(|e| panic!("Write should succeed for case {i}: {e}")); // 读取 let content = fs::read_to_string(&test_file) - .unwrap_or_else(|e| panic!("Read should succeed for case {}: {}", i, e)); + .unwrap_or_else(|e| panic!("Read should succeed for case {i}: {e}")); let parsed: serde_json::Value = serde_json::from_str(&content) - .unwrap_or_else(|e| panic!("Parse should succeed for case {}: {}", i, e)); + .unwrap_or_else(|e| panic!("Parse should succeed for case {i}: {e}")); // 验证一致性 assert_eq!( &parsed, test_data, - "Roundtrip should preserve data for case {}", - i + "Roundtrip should preserve data for case {i}" ); } } diff --git a/src-tauri/src/services/machine_id_service.rs b/src-tauri/src/services/machine_id_service.rs index ac76672d6..e6fed6a48 100644 --- a/src-tauri/src/services/machine_id_service.rs +++ b/src-tauri/src/services/machine_id_service.rs @@ -32,12 +32,12 @@ impl MachineIdService { // 确保应用数据目录存在 if let Err(e) = fs::create_dir_all(&app_data_dir) { - return Err(format!("Failed to create app data directory: {}", e)); + return Err(format!("Failed to create app data directory: {e}")); } // 确保备份目录存在 if let Err(e) = fs::create_dir_all(&backup_dir) { - return Err(format!("Failed to create backup directory: {}", e)); + return Err(format!("Failed to create backup directory: {e}")); } Ok(MachineIdService { @@ -70,7 +70,7 @@ impl MachineIdService { self.get_linux_machine_id(requires_admin, backup_exists) .await } - _ => Err(format!("Unsupported operating system: {}", os)), + _ => Err(format!("Unsupported operating system: {os}")), } } @@ -103,7 +103,7 @@ impl MachineIdService { "linux" => self.set_linux_machine_id(&formatted_id).await, _ => Ok(MachineIdResult { success: false, - message: format!("Unsupported operating system: {}", os), + message: format!("Unsupported operating system: {os}"), requires_restart: false, requires_admin: false, new_machine_id: None, @@ -178,10 +178,10 @@ impl MachineIdService { }; let backup_json = serde_json::to_string_pretty(&backup) - .map_err(|e| format!("Failed to serialize backup: {}", e))?; + .map_err(|e| format!("Failed to serialize backup: {e}"))?; let write_result = fs::write(file_path, backup_json) - .map_err(|e| format!("Failed to write backup file: {}", e)); + .map_err(|e| format!("Failed to write backup file: {e}")); // 添加到历史记录 if write_result.is_ok() { @@ -198,10 +198,10 @@ impl MachineIdService { /// 从文件恢复机器码 pub async fn restore_machine_id(&self, file_path: &str) -> Result { let backup_content = fs::read_to_string(file_path) - .map_err(|e| format!("Failed to read backup file: {}", e))?; + .map_err(|e| format!("Failed to read backup file: {e}"))?; let backup: MachineIdBackup = serde_json::from_str(&backup_content) - .map_err(|e| format!("Failed to parse backup file: {}", e))?; + .map_err(|e| format!("Failed to parse backup file: {e}"))?; self.set_machine_id(&backup.machine_id).await } @@ -418,9 +418,9 @@ impl MachineIdService { // 读取系统原始 UUID let output = Command::new("ioreg") - .args(&["-rd1", "-c", "IOPlatformExpertDevice"]) + .args(["-rd1", "-c", "IOPlatformExpertDevice"]) .output() - .map_err(|e| format!("Failed to execute ioreg: {}", e))?; + .map_err(|e| format!("Failed to execute ioreg: {e}"))?; let output_str = String::from_utf8_lossy(&output.stdout); @@ -463,7 +463,7 @@ impl MachineIdService { .join("proxycast") .join("machine-id-override"); - fs::read_to_string(override_file).map_err(|e| format!("No override file found: {}", e)) + fs::read_to_string(override_file).map_err(|e| format!("No override file found: {e}")) } #[cfg(target_os = "macos")] @@ -473,11 +473,11 @@ impl MachineIdService { .join("proxycast"); fs::create_dir_all(&override_dir) - .map_err(|e| format!("Failed to create override directory: {}", e))?; + .map_err(|e| format!("Failed to create override directory: {e}"))?; let override_file = override_dir.join("machine-id-override"); fs::write(override_file, new_id) - .map_err(|e| format!("Failed to write override file: {}", e))?; + .map_err(|e| format!("Failed to write override file: {e}"))?; Ok(()) } @@ -682,10 +682,10 @@ impl MachineIdService { }; let backup_json = serde_json::to_string_pretty(&backup) - .map_err(|e| format!("Failed to serialize backup: {}", e))?; + .map_err(|e| format!("Failed to serialize backup: {e}"))?; fs::write(backup_file, backup_json) - .map_err(|e| format!("Failed to create auto backup: {}", e))?; + .map_err(|e| format!("Failed to create auto backup: {e}"))?; Ok(()) } @@ -699,7 +699,7 @@ impl MachineIdService { } let content = fs::read_to_string(&self.history_file) - .map_err(|e| format!("Failed to read history file: {}", e))?; + .map_err(|e| format!("Failed to read history file: {e}"))?; let history: Vec = serde_json::from_str(&content).unwrap_or_else(|_| vec![]); @@ -710,10 +710,10 @@ impl MachineIdService { /// 保存历史记录 fn save_history(&self, history: &[MachineIdHistory]) -> Result<(), String> { let history_json = serde_json::to_string_pretty(history) - .map_err(|e| format!("Failed to serialize history: {}", e))?; + .map_err(|e| format!("Failed to serialize history: {e}"))?; fs::write(&self.history_file, history_json) - .map_err(|e| format!("Failed to save history: {}", e))?; + .map_err(|e| format!("Failed to save history: {e}"))?; Ok(()) } diff --git a/src-tauri/src/services/model_registry_service.rs b/src-tauri/src/services/model_registry_service.rs index d27b3a0ed..5cf3628bf 100644 --- a/src-tauri/src/services/model_registry_service.rs +++ b/src-tauri/src/services/model_registry_service.rs @@ -186,14 +186,14 @@ impl ModelRegistryService { ); if !index_file.exists() { - return Err(format!("索引文件不存在: {:?}", index_file)); + return Err(format!("索引文件不存在: {index_file:?}")); } // 1. 读取索引文件 let index_content = - std::fs::read_to_string(&index_file).map_err(|e| format!("读取索引文件失败: {}", e))?; + std::fs::read_to_string(&index_file).map_err(|e| format!("读取索引文件失败: {e}"))?; let index: RepoIndex = - serde_json::from_str(&index_content).map_err(|e| format!("解析索引文件失败: {}", e))?; + serde_json::from_str(&index_content).map_err(|e| format!("解析索引文件失败: {e}"))?; tracing::info!( "[ModelRegistry] 索引包含 {} 个 providers", @@ -208,7 +208,7 @@ impl ModelRegistryService { tracing::info!("[ModelRegistry] providers_dir: {:?}", providers_dir); for provider_id in &index.providers { - let provider_file = providers_dir.join(format!("{}.json", provider_id)); + let provider_file = providers_dir.join(format!("{provider_id}.json")); if !provider_file.exists() { tracing::warn!("[ModelRegistry] Provider 文件不存在: {:?}", provider_file); @@ -294,7 +294,7 @@ impl ModelRegistryService { let alias_files = ["kiro", "antigravity", "codex", "gemini"]; for alias_name in alias_files { - let alias_file = aliases_dir.join(format!("{}.json", alias_name)); + let alias_file = aliases_dir.join(format!("{alias_name}.json")); if !alias_file.exists() { continue; } @@ -877,13 +877,13 @@ impl ModelRegistryService { Ok(FetchModelsResult { models: vec![], source: ModelFetchSource::LocalFallback, - error: Some(format!("API 获取失败: {}, 本地也无数据", api_error)), + error: Some(format!("API 获取失败: {api_error}, 本地也无数据")), }) } else { Ok(FetchModelsResult { models: local_models, source: ModelFetchSource::LocalFallback, - error: Some(format!("API 获取失败: {}, 已使用本地数据", api_error)), + error: Some(format!("API 获取失败: {api_error}, 已使用本地数据")), }) } } @@ -901,7 +901,7 @@ impl ModelRegistryService { // 如果路径中间有 /v1/,直接追加 models format!("{}models", host.trim_end_matches('/').to_string() + "/") } else { - format!("{}/v1/models", host) + format!("{host}/v1/models") } } @@ -914,15 +914,15 @@ impl ModelRegistryService { let client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(30)) .build() - .map_err(|e| format!("创建 HTTP 客户端失败: {}", e))?; + .map_err(|e| format!("创建 HTTP 客户端失败: {e}"))?; let response = client .get(url) - .header("Authorization", format!("Bearer {}", api_key)) + .header("Authorization", format!("Bearer {api_key}")) .header("Content-Type", "application/json") .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if !response.status().is_success() { let status = response.status(); @@ -930,17 +930,17 @@ impl ModelRegistryService { .text() .await .unwrap_or_else(|_| "无法读取响应体".to_string()); - return Err(format!("API 返回错误 {}: {}", status, body)); + return Err(format!("API 返回错误 {status}: {body}")); } let body = response .text() .await - .map_err(|e| format!("读取响应失败: {}", e))?; + .map_err(|e| format!("读取响应失败: {e}"))?; // 解析 OpenAI 格式的响应 let api_response: ApiModelsResponse = - serde_json::from_str(&body).map_err(|e| format!("解析响应失败: {}", e))?; + serde_json::from_str(&body).map_err(|e| format!("解析响应失败: {e}"))?; Ok(api_response.data) } @@ -953,7 +953,12 @@ impl ModelRegistryService { now: i64, ) -> EnhancedModelMetadata { // 从 model id 推断显示名称 - let display_name = model.id.split('/').last().unwrap_or(&model.id).to_string(); + let display_name = model + .id + .split('/') + .next_back() + .unwrap_or(&model.id) + .to_string(); EnhancedModelMetadata { id: model.id.clone(), diff --git a/src-tauri/src/services/model_service.rs b/src-tauri/src/services/model_service.rs index 06e949bae..f909e4a5a 100644 --- a/src-tauri/src/services/model_service.rs +++ b/src-tauri/src/services/model_service.rs @@ -130,13 +130,13 @@ impl ModelService { let response = self .client .get(&url) - .header("Authorization", format!("Bearer {}", api_key)) + .header("Authorization", format!("Bearer {api_key}")) .timeout(self.timeout) .send() .await .map_err(|e| { tracing::error!("[MODEL_SERVICE] OpenAI 请求失败: {}", e); - format!("请求失败: {}", e) + format!("请求失败: {e}") })?; let status = response.status(); @@ -149,12 +149,12 @@ impl ModelService { status, error_body ); - return Err(format!("HTTP 错误: {}", status)); + return Err(format!("HTTP 错误: {status}")); } let response_text = response.text().await.map_err(|e| { tracing::error!("[MODEL_SERVICE] 读取 OpenAI 响应体失败: {}", e); - format!("读取响应体失败: {}", e) + format!("读取响应体失败: {e}") })?; tracing::debug!("[MODEL_SERVICE] OpenAI 响应体: {}", response_text); @@ -166,7 +166,7 @@ impl ModelService { e, response_text ); - format!("解析响应失败: {}", e) + format!("解析响应失败: {e}") })?; let model_ids: Vec = models_response.data.into_iter().map(|m| m.id).collect(); @@ -213,7 +213,7 @@ impl ModelService { .await .map_err(|e| { tracing::error!("[MODEL_SERVICE] Anthropic 请求失败: {}", e); - format!("请求失败: {}", e) + format!("请求失败: {e}") })?; let status = response.status(); @@ -226,12 +226,12 @@ impl ModelService { status, error_body ); - return Err(format!("HTTP 错误: {}", status)); + return Err(format!("HTTP 错误: {status}")); } let response_text = response.text().await.map_err(|e| { tracing::error!("[MODEL_SERVICE] 读取 Anthropic 响应体失败: {}", e); - format!("读取响应体失败: {}", e) + format!("读取响应体失败: {e}") })?; tracing::debug!("[MODEL_SERVICE] Anthropic 响应体: {}", response_text); @@ -243,7 +243,7 @@ impl ModelService { e, response_text ); - format!("解析响应失败: {}", e) + format!("解析响应失败: {e}") })?; let model_ids: Vec = models_response.data.into_iter().map(|m| m.id).collect(); @@ -278,7 +278,7 @@ impl ModelService { .await .map_err(|e| { tracing::error!("[MODEL_SERVICE] Gemini 请求失败: {}", e); - format!("请求失败: {}", e) + format!("请求失败: {e}") })?; let status = response.status(); @@ -291,12 +291,12 @@ impl ModelService { status, error_body ); - return Err(format!("HTTP 错误: {}", status)); + return Err(format!("HTTP 错误: {status}")); } let response_text = response.text().await.map_err(|e| { tracing::error!("[MODEL_SERVICE] 读取 Gemini 响应体失败: {}", e); - format!("读取响应体失败: {}", e) + format!("读取响应体失败: {e}") })?; tracing::debug!("[MODEL_SERVICE] Gemini 响应体: {}", response_text); @@ -309,7 +309,7 @@ impl ModelService { e, response_text ); - format!("解析响应失败: {}", e) + format!("解析响应失败: {e}") })?; let models = response_json diff --git a/src-tauri/src/services/provider_pool_service.rs b/src-tauri/src/services/provider_pool_service.rs index f2779e101..31700c995 100644 --- a/src-tauri/src/services/provider_pool_service.rs +++ b/src-tauri/src/services/provider_pool_service.rs @@ -89,7 +89,7 @@ impl ProviderPoolService { /// 获取所有凭证概览 pub fn get_overview(&self, db: &DbConnection) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let grouped = ProviderPoolDao::get_grouped(&conn).map_err(|e| e.to_string())?; let mut overview = Vec::new(); @@ -123,7 +123,7 @@ impl ProviderPoolService { provider_type: &str, ) -> Result, String> { let pt: PoolProviderType = provider_type.parse().map_err(|e: String| e)?; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut credentials = ProviderPoolDao::get_by_type(&conn, &pt).map_err(|e| e.to_string())?; @@ -154,7 +154,7 @@ impl ProviderPoolService { cred.check_health = check_health.unwrap_or(true); cred.check_model_name = check_model_name; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::insert(&conn, &cred).map_err(|e| e.to_string())?; Ok(cred) @@ -172,10 +172,10 @@ impl ProviderPoolService { not_supported_models: Option>, proxy_url: Option, ) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let mut cred = ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))?; + .ok_or_else(|| format!("Credential not found: {uuid}"))?; // 处理 name:空字符串表示清除,None 表示不修改 if let Some(n) = name { @@ -206,7 +206,7 @@ impl ProviderPoolService { /// 删除凭证 pub fn delete_credential(&self, db: &DbConnection, uuid: &str) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::delete(&conn, uuid).map_err(|e| e.to_string()) } @@ -242,13 +242,12 @@ impl ProviderPoolService { Ok(pt) => pt, Err(_) => { eprintln!( - "[SELECT_CREDENTIAL] 未知的 provider_type '{}', 返回 None 以便智能降级", - provider_type + "[SELECT_CREDENTIAL] 未知的 provider_type '{provider_type}', 返回 None 以便智能降级" ); return Ok(None); } }; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; // 获取凭证,对于 AI Provider 类型,也查找 Assistant 类型的凭证 let mut credentials = @@ -394,8 +393,7 @@ impl ProviderPoolService { client_type: Option<&crate::server::client_detector::ClientType>, ) -> Result, String> { eprintln!( - "[select_credential_with_fallback] 开始: provider_type={}, model={:?}, provider_id_hint={:?}", - provider_type, model, provider_id_hint + "[select_credential_with_fallback] 开始: provider_type={provider_type}, model={model:?}, provider_id_hint={provider_id_hint:?}" ); // Step 1: 尝试从 Provider Pool 选择 (OAuth + API Key) @@ -413,8 +411,7 @@ impl ProviderPoolService { // Step 2: 智能降级到 API Key Provider let pt: PoolProviderType = provider_type.parse().unwrap_or(PoolProviderType::OpenAI); eprintln!( - "[select_credential_with_fallback] 解析 provider_type '{}' -> {:?}", - provider_type, pt + "[select_credential_with_fallback] 解析 provider_type '{provider_type}' -> {pt:?}" ); // 传入 provider_id_hint 支持 60+ Provider @@ -432,8 +429,7 @@ impl ProviderPoolService { // Step 3: 都没有找到 eprintln!( - "[select_credential_with_fallback] 未找到任何凭证 for provider_type='{}'", - provider_type + "[select_credential_with_fallback] 未找到任何凭证 for provider_type='{provider_type}'" ); Ok(None) } @@ -540,10 +536,10 @@ impl ProviderPoolService { /// 记录凭证使用 pub fn record_usage(&self, db: &DbConnection, uuid: &str) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let cred = ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))?; + .ok_or_else(|| format!("Credential not found: {uuid}"))?; ProviderPoolDao::update_usage(&conn, uuid, cred.usage_count + 1, Utc::now()) .map_err(|e| e.to_string()) @@ -556,7 +552,7 @@ impl ProviderPoolService { uuid: &str, check_model: Option<&str>, ) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::update_health_status( &conn, uuid, @@ -577,10 +573,10 @@ impl ProviderPoolService { uuid: &str, error_message: Option<&str>, ) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let cred = ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))?; + .ok_or_else(|| format!("Credential not found: {uuid}"))?; let new_error_count = cred.error_count + 1; let is_healthy = new_error_count < self.max_error_count; @@ -600,7 +596,7 @@ impl ProviderPoolService { /// 重置凭证计数器 pub fn reset_counters(&self, db: &DbConnection, uuid: &str) -> Result<(), String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::reset_counters(&conn, uuid).map_err(|e| e.to_string()) } @@ -611,7 +607,7 @@ impl ProviderPoolService { provider_type: &str, ) -> Result { let pt: PoolProviderType = provider_type.parse().map_err(|e: String| e)?; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::reset_health_by_type(&conn, &pt).map_err(|e| e.to_string()) } @@ -622,7 +618,7 @@ impl ProviderPoolService { db: &DbConnection, uuid: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let cred = ProviderPoolDao::get_by_uuid(&conn, uuid).map_err(|e| e.to_string())?; Ok(cred.map(|c| CredentialHealthInfo { @@ -647,7 +643,7 @@ impl ProviderPoolService { &self, db: &DbConnection, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let credentials = ProviderPoolDao::get_all(&conn).map_err(|e| e.to_string())?; Ok(credentials @@ -680,10 +676,10 @@ impl ProviderPoolService { let error_message = error.user_message(); let requires_reauth = error.requires_reauth(); - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let cred = ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))?; + .ok_or_else(|| format!("Credential not found: {uuid}"))?; let new_error_count = cred.error_count + 1; // 如果需要重新授权,直接标记为不健康 @@ -694,7 +690,7 @@ impl ProviderPoolService { }; let error_msg = if requires_reauth { - format!("[需要重新授权] {}", error_message) + format!("[需要重新授权] {error_message}") } else { error_message }; @@ -723,7 +719,7 @@ impl ProviderPoolService { let pt: PoolProviderType = provider_type .parse() .map_err(|_| SelectionError::NoCredentials)?; - let conn = db.lock().map_err(|_| SelectionError::NoCredentials)?; + let conn = crate::database::lock_db(db).map_err(|_| SelectionError::NoCredentials)?; let credentials = ProviderPoolDao::get_by_type(&conn, &pt).map_err(|_| SelectionError::NoCredentials)?; drop(conn); @@ -833,10 +829,10 @@ impl ProviderPoolService { uuid: &str, ) -> Result { let cred = { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; let check_model = cred @@ -873,10 +869,10 @@ impl ProviderPoolService { // 重新获取凭证(token 已更新) let updated_cred = { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; // 重新执行健康检查 @@ -920,7 +916,7 @@ impl ProviderPoolService { uuid: uuid.to_string(), success: false, model: Some(check_model), - message: Some(format!("{} (Token 刷新失败: {})", e, refresh_err)), + message: Some(format!("{e} (Token 刷新失败: {refresh_err})")), duration_ms, }); } @@ -947,7 +943,7 @@ impl ProviderPoolService { ) -> Result, String> { let pt: PoolProviderType = provider_type.parse().map_err(|e: String| e)?; let credentials = { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_type(&conn, &pt).map_err(|e| e.to_string())? }; @@ -1030,24 +1026,24 @@ impl ProviderPoolService { /// 将技术错误转换为用户友好的错误信息 fn format_user_friendly_error(&self, error: &str, provider_type: &str) -> String { if error.contains("No client_id") { - format!("OAuth 配置不完整:缺少必要的认证参数。\n💡 解决方案:\n1. 检查 {} OAuth 凭证配置是否完整\n2. 如问题持续,建议删除后重新添加此凭证\n3. 或者切换到其他可用的凭证", provider_type) + format!("OAuth 配置不完整:缺少必要的认证参数。\n💡 解决方案:\n1. 检查 {provider_type} OAuth 凭证配置是否完整\n2. 如问题持续,建议删除后重新添加此凭证\n3. 或者切换到其他可用的凭证") } else if error.contains("请求失败") || error.contains("error sending request") { - format!("网络连接失败,无法访问 {} 服务。\n💡 解决方案:\n1. 检查网络连接是否正常\n2. 确认防火墙或代理设置\n3. 稍后重试,如问题持续请联系网络管理员", provider_type) + format!("网络连接失败,无法访问 {provider_type} 服务。\n💡 解决方案:\n1. 检查网络连接是否正常\n2. 确认防火墙或代理设置\n3. 稍后重试,如问题持续请联系网络管理员") } else if error.contains("HTTP 401") || error.contains("HTTP 403") { - format!("{} 认证失败,凭证可能已过期或无效。\n💡 解决方案:\n1. 点击\"刷新\"按钮尝试更新 Token\n2. 如刷新失败,请删除后重新添加此凭证\n3. 检查账户权限是否正常", provider_type) + format!("{provider_type} 认证失败,凭证可能已过期或无效。\n💡 解决方案:\n1. 点击\"刷新\"按钮尝试更新 Token\n2. 如刷新失败,请删除后重新添加此凭证\n3. 检查账户权限是否正常") } else if error.contains("HTTP 429") { - format!("{} 请求频率过高,已被限流。\n💡 解决方案:\n1. 稍等几分钟后再次尝试\n2. 考虑添加更多凭证分散负载", provider_type) + format!("{provider_type} 请求频率过高,已被限流。\n💡 解决方案:\n1. 稍等几分钟后再次尝试\n2. 考虑添加更多凭证分散负载") } else if error.contains("HTTP 500") || error.contains("HTTP 502") || error.contains("HTTP 503") { - format!("{} 服务暂时不可用。\n💡 解决方案:\n1. 这通常是服务提供方的临时问题\n2. 请稍后重试\n3. 如问题持续,可尝试其他凭证", provider_type) + format!("{provider_type} 服务暂时不可用。\n💡 解决方案:\n1. 这通常是服务提供方的临时问题\n2. 请稍后重试\n3. 如问题持续,可尝试其他凭证") } else if error.contains("读取凭证文件失败") || error.contains("解析凭证失败") { "凭证文件损坏或不可读。\n💡 解决方案:\n1. 凭证文件可能已损坏\n2. 建议删除此凭证后重新添加\n3. 确保文件权限正确且格式为有效的 JSON".to_string() } else { // 对于其他未识别的错误,提供通用建议 - format!("操作失败:{}\n💡 建议:\n1. 检查网络连接和凭证状态\n2. 尝试刷新 Token 或重新添加凭证\n3. 如问题持续,请联系技术支持", error) + format!("操作失败:{error}\n💡 建议:\n1. 检查网络连接和凭证状态\n2. 尝试刷新 Token 或重新添加凭证\n3. 如问题持续,请联系技术支持") } } @@ -1060,9 +1056,7 @@ impl ProviderPoolService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| { - self.format_user_friendly_error(&format!("加载凭证失败: {}", e), "Kiro") - })?; + .map_err(|e| self.format_user_friendly_error(&format!("加载凭证失败: {e}"), "Kiro"))?; let access_token = provider .credentials @@ -1125,7 +1119,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| self.format_user_friendly_error(&format!("请求失败: {}", e), "Kiro"))?; + .map_err(|e| self.format_user_friendly_error(&format!("请求失败: {e}"), "Kiro"))?; let status = response.status(); tracing::info!("[KIRO HEALTH] 响应状态: {}", status); @@ -1136,7 +1130,7 @@ impl ProviderPoolService { } else { let body_text = response.text().await.unwrap_or_default(); tracing::warn!("[KIRO HEALTH] 健康检查失败: {} - {}", status, body_text); - let error_msg = format!("HTTP {}: {}", status, body_text); + let error_msg = format!("HTTP {status}: {body_text}"); Err(self.format_user_friendly_error(&error_msg, "Kiro")) } } @@ -1151,9 +1145,9 @@ impl ProviderPoolService { _model: &str, ) -> Result<(), String> { let creds_content = - std::fs::read_to_string(creds_path).map_err(|e| format!("读取凭证文件失败: {}", e))?; + std::fs::read_to_string(creds_path).map_err(|e| format!("读取凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["access_token"] .as_str() @@ -1182,14 +1176,14 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) } else { let status = response.status(); let body = response.text().await.unwrap_or_default(); - Err(format!("HTTP {} - {}", status, body)) + Err(format!("HTTP {status} - {body}")) } } @@ -1201,9 +1195,9 @@ impl ProviderPoolService { _model: &str, ) -> Result<(), String> { let creds_content = - std::fs::read_to_string(creds_path).map_err(|e| format!("读取凭证文件失败: {}", e))?; + std::fs::read_to_string(creds_path).map_err(|e| format!("读取凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&creds_content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["access_token"] .as_str() @@ -1222,7 +1216,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1246,9 +1240,9 @@ impl ProviderPoolService { // 如果用户输入了带 /v1 的 URL,直接使用;否则拼接 /v1 let url = if base.ends_with("/v1") { - format!("{}/chat/completions", base) + format!("{base}/chat/completions") } else { - format!("{}/v1/chat/completions", base) + format!("{base}/v1/chat/completions") }; let request_body = serde_json::json!({ @@ -1267,7 +1261,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1298,9 +1292,9 @@ impl ProviderPoolService { // 如果用户输入了带 /v1 的 URL,直接使用;否则拼接 /v1 let url = if base.ends_with("/v1") { - format!("{}/messages", base) + format!("{base}/messages") } else { - format!("{}/v1/messages", base) + format!("{base}/v1/messages") }; let request_body = serde_json::json!({ @@ -1320,7 +1314,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1343,7 +1337,7 @@ impl ProviderPoolService { model: &str, ) -> Result<(), String> { let base = base_url.unwrap_or("https://generativelanguage.googleapis.com/v1beta"); - let url = format!("{}/models/{}:generateContent", base, model); + let url = format!("{base}/models/{model}:generateContent"); let request_body = serde_json::json!({ "contents": [{"role": "user", "parts": [{"text": "Say OK"}]}], @@ -1358,7 +1352,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1375,7 +1369,7 @@ impl ProviderPoolService { model: &str, ) -> Result<(), String> { let base = base_url.unwrap_or("https://generativelanguage.googleapis.com"); - let url = format!("{}/v1beta/models/{}:generateContent", base, model); + let url = format!("{base}/v1beta/models/{model}:generateContent"); let request_body = serde_json::json!({ "contents": [{"role": "user", "parts": [{"text": "Say OK"}]}], @@ -1390,7 +1384,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1413,14 +1407,12 @@ impl ProviderPoolService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Codex 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Codex 凭证失败: {e}"))?; - let token = provider.ensure_valid_token().await.map_err(|e| { - format!( - "获取 Codex Token 失败: 配置错误,请检查凭证设置。详情:{}", - e - ) - })?; + let token = provider + .ensure_valid_token() + .await + .map_err(|e| format!("获取 Codex Token 失败: 配置错误,请检查凭证设置。详情:{e}"))?; // 优先使用 override_base_url(来自 CredentialData),其次使用凭证文件中的配置 let base_url = override_base_url @@ -1492,7 +1484,7 @@ impl ProviderPoolService { .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1515,12 +1507,12 @@ impl ProviderPoolService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Claude OAuth 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Claude OAuth 凭证失败: {e}"))?; let token = provider .ensure_valid_token() .await - .map_err(|e| format!("获取 Claude OAuth Token 失败: {}", e))?; + .map_err(|e| format!("获取 Claude OAuth Token 失败: {e}"))?; // 使用 Anthropic API 进行健康检查 let url = "https://api.anthropic.com/v1/messages"; @@ -1533,13 +1525,13 @@ impl ProviderPoolService { let response = self .client .post(url) - .header("Authorization", format!("Bearer {}", token)) + .header("Authorization", format!("Bearer {token}")) .header("anthropic-version", "2023-06-01") .json(&request_body) .timeout(self.health_check_timeout) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if response.status().is_success() { Ok(()) @@ -1554,7 +1546,7 @@ impl ProviderPoolService { db: &DbConnection, name: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_name(&conn, name).map_err(|e| e.to_string()) } @@ -1564,7 +1556,7 @@ impl ProviderPoolService { db: &DbConnection, uuid: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_uuid(&conn, uuid).map_err(|e| e.to_string()) } @@ -1574,7 +1566,7 @@ impl ProviderPoolService { db: &DbConnection, base_url: &str, ) -> Result, String> { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let grouped = ProviderPoolDao::get_grouped(&conn).map_err(|e| e.to_string())?; drop(conn); @@ -1624,9 +1616,9 @@ impl ProviderPoolService { provider_type: &str, ) -> Result { let content = - std::fs::read_to_string(creds_path).map_err(|e| format!("读取凭证文件失败: {}", e))?; + std::fs::read_to_string(creds_path).map_err(|e| format!("读取凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证文件失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证文件失败: {e}"))?; let has_api_key = creds .get("apiKey") @@ -1695,14 +1687,13 @@ impl ProviderPoolService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| { - self.format_user_friendly_error(&format!("加载凭证失败: {}", e), "Kiro") - })?; + .map_err(|e| self.format_user_friendly_error(&format!("加载凭证失败: {e}"), "Kiro"))?; // 使用副本文件中的凭证刷新 Token - provider.refresh_token().await.map_err(|e| { - self.format_user_friendly_error(&format!("刷新 Token 失败: {}", e), "Kiro") - }) + provider + .refresh_token() + .await + .map_err(|e| self.format_user_friendly_error(&format!("刷新 Token 失败: {e}"), "Kiro")) } /// 刷新 OAuth Token (Gemini) @@ -1711,11 +1702,11 @@ impl ProviderPoolService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载凭证失败: {}", e))?; + .map_err(|e| format!("加载凭证失败: {e}"))?; provider .refresh_token() .await - .map_err(|e| format!("刷新 Token 失败: {}", e)) + .map_err(|e| format!("刷新 Token 失败: {e}")) } /// 刷新 OAuth Token (Antigravity) @@ -1724,11 +1715,11 @@ impl ProviderPoolService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载凭证失败: {}", e))?; + .map_err(|e| format!("加载凭证失败: {e}"))?; provider .refresh_token() .await - .map_err(|e| format!("刷新 Token 失败: {}", e)) + .map_err(|e| format!("刷新 Token 失败: {e}")) } /// 刷新凭证池中指定凭证的 OAuth Token @@ -1738,10 +1729,10 @@ impl ProviderPoolService { uuid: &str, ) -> Result { let cred = { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; match &cred.credential { @@ -1765,10 +1756,10 @@ impl ProviderPoolService { uuid: &str, ) -> Result { let cred = { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; let creds_path = get_oauth_creds_path(&cred.credential) @@ -1795,7 +1786,7 @@ impl ProviderPoolService { cred.check_health = check_health.unwrap_or(true); cred.check_model_name = check_model_name; - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; ProviderPoolDao::insert(&conn, &cred).map_err(|e| e.to_string())?; Ok(cred) @@ -1834,7 +1825,7 @@ impl ProviderPoolService { CredentialSource::Private, ) { Ok(_) => result.migrated_count += 1, - Err(e) => result.errors.push(format!("Kiro: {}", e)), + Err(e) => result.errors.push(format!("Kiro: {e}")), } } else { result.skipped_count += 1; @@ -1863,7 +1854,7 @@ impl ProviderPoolService { CredentialSource::Private, ) { Ok(_) => result.migrated_count += 1, - Err(e) => result.errors.push(format!("Gemini: {}", e)), + Err(e) => result.errors.push(format!("Gemini: {e}")), } } else { result.skipped_count += 1; @@ -1889,7 +1880,7 @@ impl ProviderPoolService { CredentialSource::Private, ) { Ok(_) => result.migrated_count += 1, - Err(e) => result.errors.push(format!("OpenAI: {}", e)), + Err(e) => result.errors.push(format!("OpenAI: {e}")), } } else { result.skipped_count += 1; @@ -1914,7 +1905,7 @@ impl ProviderPoolService { CredentialSource::Private, ) { Ok(_) => result.migrated_count += 1, - Err(e) => result.errors.push(format!("Claude: {}", e)), + Err(e) => result.errors.push(format!("Claude: {e}")), } } else { result.skipped_count += 1; @@ -1927,7 +1918,7 @@ impl ProviderPoolService { /// 检查是否存在相同路径的凭证 fn credential_exists_by_path(&self, db: &DbConnection, path: &str) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let all_creds = ProviderPoolDao::get_all(&conn).map_err(|e| e.to_string())?; for cred in all_creds { @@ -1946,7 +1937,7 @@ impl ProviderPoolService { db: &DbConnection, api_key: &str, ) -> Result { - let conn = db.lock().map_err(|e| e.to_string())?; + let conn = crate::database::lock_db(db)?; let all_creds = ProviderPoolDao::get_all(&conn).map_err(|e| e.to_string())?; for cred in all_creds { diff --git a/src-tauri/src/services/session_context_service.rs b/src-tauri/src/services/session_context_service.rs index 926fab975..8d054072a 100644 --- a/src-tauri/src/services/session_context_service.rs +++ b/src-tauri/src/services/session_context_service.rs @@ -81,7 +81,7 @@ impl SessionContextService { // 获取所有消息 let all_messages = GeneralChatDao::get_messages(&conn, session_id, None, None) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; if all_messages.is_empty() { return Ok(vec![]); @@ -113,7 +113,7 @@ impl SessionContextService { if let Ok(summary) = self.get_or_create_summary(session_id, &all_messages) { // 添加摘要作为系统消息 let summary_message = ChatMessage { - id: format!("summary-{}", session_id), + id: format!("summary-{session_id}"), session_id: session_id.to_string(), role: MessageRole::System, content: format!( @@ -341,10 +341,10 @@ impl SessionContextService { let conn = self.db_connection.lock().map_err(|e| e.to_string())?; let message_count = GeneralChatDao::get_message_count(&conn, session_id) - .map_err(|e| format!("获取消息数量失败: {}", e))? as usize; + .map_err(|e| format!("获取消息数量失败: {e}"))? as usize; let messages = GeneralChatDao::get_messages(&conn, session_id, None, None) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; let total_characters: usize = messages.iter().map(|m| m.content.len()).sum(); let user_messages = messages @@ -382,7 +382,7 @@ impl SessionContextService { let conn = self.db_connection.lock().map_err(|e| e.to_string())?; let messages = GeneralChatDao::get_messages(&conn, session_id, None, None) - .map_err(|e| format!("获取消息失败: {}", e))?; + .map_err(|e| format!("获取消息失败: {e}"))?; if messages.len() > self.config.summary_threshold { debug!("为会话 {} 预热上下文", session_id); diff --git a/src-tauri/src/services/skill_service.rs b/src-tauri/src/services/skill_service.rs index 8fe42bdae..ae9c003a0 100644 --- a/src-tauri/src/services/skill_service.rs +++ b/src-tauri/src/services/skill_service.rs @@ -88,7 +88,7 @@ impl SkillService { let already_exists = all_skills.values().any(|s| s.directory == directory); if !already_exists { - let key = format!("local:{}", directory); + let key = format!("local:{directory}"); let skill_md = entry.path().join("SKILL.md"); let (name, description) = if skill_md.exists() { self.parse_skill_metadata(&skill_md) @@ -186,7 +186,7 @@ impl SkillService { let name = metadata.name.unwrap_or_else(|| directory.clone()); let description = metadata.description.unwrap_or_default(); - let key = format!("{}{}", repo_key_prefix, directory); + let key = format!("{repo_key_prefix}{directory}"); let app_key = format!("{}:{}", app_type.to_string().to_lowercase(), directory); let installed = installed_states .get(&app_key) @@ -246,8 +246,7 @@ impl SkillService { for branch in branches { let zip_url = format!( - "https://github.com/{}/{}/archive/refs/heads/{}.zip", - repo_owner, repo_name, branch + "https://github.com/{repo_owner}/{repo_name}/archive/refs/heads/{branch}.zip" ); match self @@ -288,7 +287,7 @@ impl SkillService { let mut archive = zip::ZipArchive::new(cursor).context("Failed to open ZIP")?; // 查找技能目录 - let skill_prefix = format!("/{}/", directory); + let skill_prefix = format!("/{directory}/"); let mut found = false; for i in 0..archive.len() { diff --git a/src-tauri/src/services/switch.rs b/src-tauri/src/services/switch.rs index 55d0c1c6f..b28f899af 100644 --- a/src-tauri/src/services/switch.rs +++ b/src-tauri/src/services/switch.rs @@ -151,11 +151,11 @@ impl SwitchService { warn!("尝试恢复原配置: {}", current.name); if let Err(restore_error) = live_sync::sync_to_live(&app_type_enum, current) { error!("恢复原配置失败: {}", restore_error); - return Err(format!("切换失败且无法恢复原配置: {}", sync_error)); + return Err(format!("切换失败且无法恢复原配置: {sync_error}")); } } - return Err(format!("配置同步失败: {}", sync_error)); + return Err(format!("配置同步失败: {sync_error}")); } } diff --git a/src-tauri/src/services/sysinfo_service.rs b/src-tauri/src/services/sysinfo_service.rs index 77cd05ab1..ff753322e 100644 --- a/src-tauri/src/services/sysinfo_service.rs +++ b/src-tauri/src/services/sysinfo_service.rs @@ -82,7 +82,7 @@ impl SysinfoService { // 各核心 CPU 使用率 let mut cpu_cores = std::collections::HashMap::new(); for (i, cpu) in system.cpus().iter().enumerate() { - cpu_cores.insert(format!("cpu:{}", i), cpu.cpu_usage()); + cpu_cores.insert(format!("cpu:{i}"), cpu.cpu_usage()); } SysinfoData { diff --git a/src-tauri/src/services/token_cache_service.rs b/src-tauri/src/services/token_cache_service.rs index 260ab98d7..c501f4131 100644 --- a/src-tauri/src/services/token_cache_service.rs +++ b/src-tauri/src/services/token_cache_service.rs @@ -124,7 +124,7 @@ impl TokenCacheService { let conn = db.lock().map_err(|e| e.to_string())?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; // 尝试从源文件读取 accessToken @@ -275,7 +275,7 @@ impl TokenCacheService { let conn = db.lock().map_err(|e| e.to_string())?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; tracing::info!( @@ -355,7 +355,7 @@ impl TokenCacheService { let conn = db.lock().map_err(|e| e.to_string())?; // 简化禁用逻辑:直接在数据库中标记为禁用 let sql = "UPDATE credentials SET is_disabled = true WHERE uuid = ?"; - conn.execute(sql, &[&uuid]).map_err(|e| e.to_string()) + conn.execute(sql, [&uuid]).map_err(|e| e.to_string()) }; match disable_result { @@ -504,12 +504,12 @@ impl TokenCacheService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Kiro 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Kiro 凭证失败: {e}"))?; let token = provider .refresh_token() .await - .map_err(|e| format!("刷新 Kiro Token 失败: {}", e))?; + .map_err(|e| format!("刷新 Kiro Token 失败: {e}"))?; // Kiro token 通常 1 小时过期,我们假设 50 分钟 let expiry_time = Utc::now() + chrono::Duration::minutes(50); @@ -530,12 +530,12 @@ impl TokenCacheService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Gemini 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Gemini 凭证失败: {e}"))?; let token = provider .refresh_token() .await - .map_err(|e| format!("刷新 Gemini Token 失败: {}", e))?; + .map_err(|e| format!("刷新 Gemini Token 失败: {e}"))?; // Gemini token 通常 1 小时过期 let expiry_time = provider @@ -562,12 +562,12 @@ impl TokenCacheService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Antigravity 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Antigravity 凭证失败: {e}"))?; let token = provider .refresh_token() .await - .map_err(|e| format!("刷新 Antigravity Token 失败: {}", e))?; + .map_err(|e| format!("刷新 Antigravity Token 失败: {e}"))?; // Antigravity token 通常 1 小时过期 let expiry_time = provider @@ -594,12 +594,12 @@ impl TokenCacheService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Codex 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Codex 凭证失败: {e}"))?; let token = provider .refresh_token_with_retry(3) .await - .map_err(|e| format!("刷新 Codex Token 失败: {}", e))?; + .map_err(|e| format!("刷新 Codex Token 失败: {e}"))?; // 解析过期时间 let expiry_time = provider @@ -628,12 +628,12 @@ impl TokenCacheService { provider .load_credentials_from_path(creds_path) .await - .map_err(|e| format!("加载 Claude OAuth 凭证失败: {}", e))?; + .map_err(|e| format!("加载 Claude OAuth 凭证失败: {e}"))?; let token = provider .refresh_token_with_retry(3) .await - .map_err(|e| format!("刷新 Claude OAuth Token 失败: {}", e))?; + .map_err(|e| format!("刷新 Claude OAuth Token 失败: {e}"))?; // 解析过期时间 let expiry_time = provider @@ -664,7 +664,7 @@ impl TokenCacheService { let conn = db.lock().map_err(|e| e.to_string())?; ProviderPoolDao::get_by_uuid(&conn, uuid) .map_err(|e| e.to_string())? - .ok_or_else(|| format!("Credential not found: {}", uuid))? + .ok_or_else(|| format!("Credential not found: {uuid}"))? }; // 尝试从源文件读取 token @@ -691,9 +691,9 @@ impl TokenCacheService { CredentialData::KiroOAuth { creds_file_path } => { let content = tokio::fs::read_to_string(creds_file_path) .await - .map_err(|e| format!("读取 Kiro 凭证文件失败: {}", e))?; + .map_err(|e| format!("读取 Kiro 凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["accessToken"] .as_str() @@ -718,9 +718,9 @@ impl TokenCacheService { } => { let content = tokio::fs::read_to_string(creds_file_path) .await - .map_err(|e| format!("读取 Gemini 凭证文件失败: {}", e))?; + .map_err(|e| format!("读取 Gemini 凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["access_token"].as_str().map(|s| s.to_string()); let refresh_token = creds["refresh_token"].as_str().map(|s| s.to_string()); @@ -742,9 +742,9 @@ impl TokenCacheService { } => { let content = tokio::fs::read_to_string(creds_file_path) .await - .map_err(|e| format!("读取 Antigravity 凭证文件失败: {}", e))?; + .map_err(|e| format!("读取 Antigravity 凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["access_token"].as_str().map(|s| s.to_string()); let refresh_token = creds["refresh_token"].as_str().map(|s| s.to_string()); @@ -798,9 +798,9 @@ impl TokenCacheService { } => { let content = tokio::fs::read_to_string(creds_file_path) .await - .map_err(|e| format!("读取 Codex 凭证文件失败: {}", e))?; + .map_err(|e| format!("读取 Codex 凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["access_token"].as_str().map(|s| s.to_string()); let refresh_token = creds["refresh_token"].as_str().map(|s| s.to_string()); @@ -821,9 +821,9 @@ impl TokenCacheService { CredentialData::ClaudeOAuth { creds_file_path } => { let content = tokio::fs::read_to_string(creds_file_path) .await - .map_err(|e| format!("读取 Claude OAuth 凭证文件失败: {}", e))?; + .map_err(|e| format!("读取 Claude OAuth 凭证文件失败: {e}"))?; let creds: serde_json::Value = - serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {}", e))?; + serde_json::from_str(&content).map_err(|e| format!("解析凭证失败: {e}"))?; let access_token = creds["access_token"].as_str().map(|s| s.to_string()); let refresh_token = creds["refresh_token"].as_str().map(|s| s.to_string()); @@ -890,7 +890,7 @@ impl TokenCacheService { let hash_value = hasher.finish(); // 生成0-30秒的延迟(转换为毫秒) - (hash_value % 30000) as u64 + hash_value % 30000 } /// 智能错误分类方法 diff --git a/src-tauri/src/services/tool_hooks_service.rs b/src-tauri/src/services/tool_hooks_service.rs index 4b7c131a6..13073c430 100644 --- a/src-tauri/src/services/tool_hooks_service.rs +++ b/src-tauri/src/services/tool_hooks_service.rs @@ -302,22 +302,20 @@ impl ToolHooksService { /// 评估单个条件 fn evaluate_single_condition(&self, condition: &HookCondition, context: &HookContext) -> bool { match condition { - HookCondition::ToolNameEquals(name) => { - context.tool_name.as_ref().map_or(false, |tn| tn == name) - } + HookCondition::ToolNameEquals(name) => context.tool_name.as_ref() == Some(name), HookCondition::ToolNameContains(substring) => context .tool_name .as_ref() - .map_or(false, |tn| tn.contains(substring)), + .is_some_and(|tn| tn.contains(substring)), HookCondition::MessageContains(substring) => { context .message_content .as_ref() - .map_or(false, |mc| mc.contains(substring)) + .is_some_and(|mc| mc.contains(substring)) || context .tool_result .as_ref() - .map_or(false, |tr| tr.contains(substring)) + .is_some_and(|tr| tr.contains(substring)) } HookCondition::MessageCountGreaterThan(count) => context.message_count > *count, HookCondition::ErrorCountGreaterThan(_count) => { @@ -471,7 +469,7 @@ impl ToolHooksService { // 替换元数据变量 for (key, value) in &context.metadata { - result = result.replace(&format!("{{{}}}", key), value); + result = result.replace(&format!("{{{key}}}"), value); } result @@ -480,9 +478,7 @@ impl ToolHooksService { /// 更新执行统计 fn update_execution_stats(&self, rule_id: &str, success: bool, execution_time_ms: f64) { let mut stats = self.execution_stats.lock().unwrap(); - let entry = stats - .entry(rule_id.to_string()) - .or_insert_with(Default::default); + let entry = stats.entry(rule_id.to_string()).or_default(); entry.execution_count += 1; if success { @@ -522,7 +518,7 @@ impl ToolHooksService { rules.retain(|r| r.id != rule_id); if rules.len() == initial_len { - return Err(format!("未找到钩子规则: {}", rule_id)); + return Err(format!("未找到钩子规则: {rule_id}")); } info!("已移除钩子规则: {}", rule_id); @@ -542,7 +538,7 @@ impl ToolHooksService { ); Ok(()) } else { - Err(format!("未找到钩子规则: {}", rule_id)) + Err(format!("未找到钩子规则: {rule_id}")) } } diff --git a/src-tauri/src/services/update_check_service.rs b/src-tauri/src/services/update_check_service.rs index 4a69dd0be..6146c7261 100644 --- a/src-tauri/src/services/update_check_service.rs +++ b/src-tauri/src/services/update_check_service.rs @@ -40,7 +40,7 @@ pub struct UpdateInfo { } /// 更新检查服务状态 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct UpdateCheckState { /// 是否正在检查 pub is_checking: bool, @@ -50,16 +50,6 @@ pub struct UpdateCheckState { pub next_check_at: Option, } -impl Default for UpdateCheckState { - fn default() -> Self { - Self { - is_checking: false, - last_result: None, - next_check_at: None, - } - } -} - /// 更新检查服务 pub struct UpdateCheckService { state: Arc>, @@ -150,7 +140,7 @@ impl UpdateCheckService { download_url: None, release_notes_url: None, checked_at: now, - error: Some(format!("创建 HTTP 客户端失败: {}", e)), + error: Some(format!("创建 HTTP 客户端失败: {e}")), }; } }; @@ -195,7 +185,7 @@ impl UpdateCheckService { download_url: None, release_notes_url: None, checked_at: now, - error: Some(format!("解析响应失败: {}", e)), + error: Some(format!("解析响应失败: {e}")), }, } } else { @@ -217,7 +207,7 @@ impl UpdateCheckService { download_url: None, release_notes_url: None, checked_at: now, - error: Some(format!("网络请求失败: {}", e)), + error: Some(format!("网络请求失败: {e}")), }, } } diff --git a/src-tauri/src/services/update_window.rs b/src-tauri/src/services/update_window.rs index d396b7981..693c9f144 100644 --- a/src-tauri/src/services/update_window.rs +++ b/src-tauri/src/services/update_window.rs @@ -145,18 +145,18 @@ pub fn open_update_window( let _ = window.set_position(LogicalPosition::new(x, y)); // 使用 JavaScript 导航到新的 URL - let js = format!("window.location.href = '{}';", url); + let js = format!("window.location.href = '{url}';"); window .eval(&js) - .map_err(|e| UpdateWindowError::OperationFailed(format!("导航失败: {}", e)))?; + .map_err(|e| UpdateWindowError::OperationFailed(format!("导航失败: {e}")))?; window .show() - .map_err(|e| UpdateWindowError::OperationFailed(format!("显示窗口失败: {}", e)))?; + .map_err(|e| UpdateWindowError::OperationFailed(format!("显示窗口失败: {e}")))?; window .set_focus() - .map_err(|e| UpdateWindowError::OperationFailed(format!("聚焦窗口失败: {}", e)))?; + .map_err(|e| UpdateWindowError::OperationFailed(format!("聚焦窗口失败: {e}")))?; return Ok(()); } @@ -178,7 +178,7 @@ pub fn open_update_window( .transparent(true) .resizable(false) .build() - .map_err(|e| UpdateWindowError::CreateFailed(format!("{}", e)))?; + .map_err(|e| UpdateWindowError::CreateFailed(format!("{e}")))?; // macOS: 设置窗口背景透明 #[cfg(target_os = "macos")] @@ -208,7 +208,7 @@ pub fn close_update_window(app: &AppHandle) -> Result<(), UpdateWindowError> { if let Some(window) = app.get_webview_window(UPDATE_WINDOW_LABEL) { window .close() - .map_err(|e| UpdateWindowError::OperationFailed(format!("关闭窗口失败: {}", e)))?; + .map_err(|e| UpdateWindowError::OperationFailed(format!("关闭窗口失败: {e}")))?; info!("更新提醒窗口已关闭"); } else { debug!("更新提醒窗口不存在,无需关闭"); diff --git a/src-tauri/src/services/usage_service.rs b/src-tauri/src/services/usage_service.rs index dca57c8f2..692d3efd5 100644 --- a/src-tauri/src/services/usage_service.rs +++ b/src-tauri/src/services/usage_service.rs @@ -166,20 +166,19 @@ pub fn build_request_headers( let mut headers = HeaderMap::new(); // Authorization header - let auth_value = format!("Bearer {}", access_token); + let auth_value = format!("Bearer {access_token}"); headers.insert("Authorization", HeaderValue::from_str(&auth_value)?); // User-Agent header // 格式: aws-sdk-js/1.0.0 ua/2.1 os/{os} lang/rust api/codewhispererruntime#1.0.0 m/N,E KiroIDE-{version}-{machineId} let os_name = std::env::consts::OS; let user_agent = format!( - "aws-sdk-js/1.0.0 ua/2.1 os/{} lang/rust api/codewhispererruntime#1.0.0 m/N,E KiroIDE-{}-{}", - os_name, kiro_version, machine_id + "aws-sdk-js/1.0.0 ua/2.1 os/{os_name} lang/rust api/codewhispererruntime#1.0.0 m/N,E KiroIDE-{kiro_version}-{machine_id}" ); headers.insert(USER_AGENT, HeaderValue::from_str(&user_agent)?); // x-amz-user-agent header - let x_amz_user_agent = format!("aws-sdk-js/1.0.0 KiroIDE-{}-{}", kiro_version, machine_id); + let x_amz_user_agent = format!("aws-sdk-js/1.0.0 KiroIDE-{kiro_version}-{machine_id}"); headers.insert( "x-amz-user-agent", HeaderValue::from_str(&x_amz_user_agent)?, @@ -208,15 +207,14 @@ pub fn build_request_headers( pub fn build_user_agent(kiro_version: &str, machine_id: &str) -> String { let os_name = std::env::consts::OS; format!( - "aws-sdk-js/1.0.0 ua/2.1 os/{} lang/rust api/codewhispererruntime#1.0.0 m/N,E KiroIDE-{}-{}", - os_name, kiro_version, machine_id + "aws-sdk-js/1.0.0 ua/2.1 os/{os_name} lang/rust api/codewhispererruntime#1.0.0 m/N,E KiroIDE-{kiro_version}-{machine_id}" ) } /// 构造 x-amz-user-agent 字符串(用于测试) #[cfg(test)] pub fn build_x_amz_user_agent(kiro_version: &str, machine_id: &str) -> String { - format!("aws-sdk-js/1.0.0 KiroIDE-{}-{}", kiro_version, machine_id) + format!("aws-sdk-js/1.0.0 KiroIDE-{kiro_version}-{machine_id}") } // ============================================================================ @@ -271,7 +269,7 @@ pub async fn get_usage_limits( let status = response.status(); if !status.is_success() { let body = response.text().await.unwrap_or_default(); - return Err(format!("API request failed with status {}: {}", status, body).into()); + return Err(format!("API request failed with status {status}: {body}").into()); } // 解析响应 @@ -615,7 +613,7 @@ mod tests { /// 生成有效的 profileArn fn arb_profile_arn() -> impl Strategy { - "[a-zA-Z0-9:/-]{10,50}".prop_map(|s| format!("arn:aws:iam::{}", s)) + "[a-zA-Z0-9:/-]{10,50}".prop_map(|s| format!("arn:aws:iam::{s}")) } proptest! { @@ -891,7 +889,7 @@ mod tests { "User-Agent should contain 'm/N,E', got: {}", user_agent); // 验证包含 KiroIDE-{version}-{machineId} - let kiro_suffix = format!("KiroIDE-{}-{}", kiro_version, machine_id); + let kiro_suffix = format!("KiroIDE-{kiro_version}-{machine_id}"); prop_assert!(user_agent.ends_with(&kiro_suffix), "User-Agent should end with '{}', got: {}", kiro_suffix, user_agent); } @@ -909,7 +907,7 @@ mod tests { let x_amz_user_agent = build_x_amz_user_agent(&kiro_version, &machine_id); // 验证格式 - let expected = format!("aws-sdk-js/1.0.0 KiroIDE-{}-{}", kiro_version, machine_id); + let expected = format!("aws-sdk-js/1.0.0 KiroIDE-{kiro_version}-{machine_id}"); prop_assert_eq!(x_amz_user_agent, expected, "x-amz-user-agent format mismatch"); } diff --git a/src-tauri/src/session/rate_limit.rs b/src-tauri/src/session/rate_limit.rs index 75c76fd74..092423c1e 100644 --- a/src-tauri/src/session/rate_limit.rs +++ b/src-tauri/src/session/rate_limit.rs @@ -125,7 +125,7 @@ impl RateLimitTracker { // 根据是否有模型信息决定存储位置 if let Some(m) = model { - let key = format!("{}:{}", account_id, m); + let key = format!("{account_id}:{m}"); self.model_limits.insert(key, record.clone()); } else { self.account_limits @@ -160,7 +160,7 @@ impl RateLimitTracker { /// 检查特定模型是否被限流 pub fn is_model_rate_limited(&self, account_id: &str, model: &str) -> bool { - let key = format!("{}:{}", account_id, model); + let key = format!("{account_id}:{model}"); if let Some(record) = self.model_limits.get(&key) { return Utc::now() < record.reset_at; } @@ -180,7 +180,7 @@ impl RateLimitTracker { /// 获取模型的剩余等待时间(秒) pub fn get_model_remaining_wait(&self, account_id: &str, model: &str) -> i64 { - let key = format!("{}:{}", account_id, model); + let key = format!("{account_id}:{model}"); if let Some(record) = self.model_limits.get(&key) { let remaining = (record.reset_at - Utc::now()).num_seconds(); if remaining > 0 { @@ -201,7 +201,7 @@ impl RateLimitTracker { /// 清除模型的限流状态 pub fn clear_model_rate_limit(&self, account_id: &str, model: &str) { - let key = format!("{}:{}", account_id, model); + let key = format!("{account_id}:{model}"); self.model_limits.remove(&key); } diff --git a/src-tauri/src/session_files/storage.rs b/src-tauri/src/session_files/storage.rs index ba9f9c42e..5077ed031 100644 --- a/src-tauri/src/session_files/storage.rs +++ b/src-tauri/src/session_files/storage.rs @@ -21,13 +21,13 @@ impl SessionFileStorage { /// 默认使用 ~/.proxycast/sessions 目录 pub fn new() -> Result { let base_dir = Self::get_default_base_dir()?; - fs::create_dir_all(&base_dir).map_err(|e| format!("创建会话存储目录失败: {}", e))?; + fs::create_dir_all(&base_dir).map_err(|e| format!("创建会话存储目录失败: {e}"))?; Ok(Self { base_dir }) } /// 使用指定目录创建存储服务 pub fn with_base_dir(base_dir: PathBuf) -> Result { - fs::create_dir_all(&base_dir).map_err(|e| format!("创建会话存储目录失败: {}", e))?; + fs::create_dir_all(&base_dir).map_err(|e| format!("创建会话存储目录失败: {e}"))?; Ok(Self { base_dir }) } @@ -62,7 +62,7 @@ impl SessionFileStorage { let files_dir = self.get_files_dir(session_id); // 创建目录 - fs::create_dir_all(&files_dir).map_err(|e| format!("创建会话目录失败: {}", e))?; + fs::create_dir_all(&files_dir).map_err(|e| format!("创建会话目录失败: {e}"))?; // 创建元数据 let meta = SessionMeta::new(session_id.to_string()); @@ -90,7 +90,7 @@ impl SessionFileStorage { pub fn delete_session(&self, session_id: &str) -> Result<(), String> { let session_dir = self.get_session_dir(session_id); if session_dir.exists() { - fs::remove_dir_all(&session_dir).map_err(|e| format!("删除会话目录失败: {}", e))?; + fs::remove_dir_all(&session_dir).map_err(|e| format!("删除会话目录失败: {e}"))?; tracing::info!("[SessionFileStorage] 删除会话目录: {:?}", session_dir); } Ok(()) @@ -104,8 +104,7 @@ impl SessionFileStorage { return Ok(sessions); } - let entries = - fs::read_dir(&self.base_dir).map_err(|e| format!("读取会话目录失败: {}", e))?; + let entries = fs::read_dir(&self.base_dir).map_err(|e| format!("读取会话目录失败: {e}"))?; for entry in entries.flatten() { if entry.path().is_dir() { @@ -140,17 +139,16 @@ impl SessionFileStorage { /// 读取会话元数据 pub fn get_meta(&self, session_id: &str) -> Result { let meta_path = self.get_meta_path(session_id); - let content = - fs::read_to_string(&meta_path).map_err(|e| format!("读取元数据失败: {}", e))?; - serde_json::from_str(&content).map_err(|e| format!("解析元数据失败: {}", e)) + let content = fs::read_to_string(&meta_path).map_err(|e| format!("读取元数据失败: {e}"))?; + serde_json::from_str(&content).map_err(|e| format!("解析元数据失败: {e}")) } /// 保存会话元数据 pub fn save_meta(&self, session_id: &str, meta: &SessionMeta) -> Result<(), String> { let meta_path = self.get_meta_path(session_id); let content = - serde_json::to_string_pretty(meta).map_err(|e| format!("序列化元数据失败: {}", e))?; - fs::write(&meta_path, content).map_err(|e| format!("写入元数据失败: {}", e)) + serde_json::to_string_pretty(meta).map_err(|e| format!("序列化元数据失败: {e}"))?; + fs::write(&meta_path, content).map_err(|e| format!("写入元数据失败: {e}")) } /// 更新会话元数据 @@ -196,7 +194,7 @@ impl SessionFileStorage { let file_path = files_dir.join(file_name); // 写入文件 - fs::write(&file_path, content).map_err(|e| format!("写入文件失败: {}", e))?; + fs::write(&file_path, content).map_err(|e| format!("写入文件失败: {e}"))?; let now = Utc::now().timestamp_millis(); let size = content.len() as u64; @@ -222,14 +220,14 @@ impl SessionFileStorage { /// 读取会话文件内容 pub fn read_file(&self, session_id: &str, file_name: &str) -> Result { let file_path = self.get_files_dir(session_id).join(file_name); - fs::read_to_string(&file_path).map_err(|e| format!("读取文件失败: {}", e)) + fs::read_to_string(&file_path).map_err(|e| format!("读取文件失败: {e}")) } /// 删除会话文件 pub fn delete_file(&self, session_id: &str, file_name: &str) -> Result<(), String> { let file_path = self.get_files_dir(session_id).join(file_name); if file_path.exists() { - fs::remove_file(&file_path).map_err(|e| format!("删除文件失败: {}", e))?; + fs::remove_file(&file_path).map_err(|e| format!("删除文件失败: {e}"))?; self.refresh_meta_stats(session_id)?; } Ok(()) @@ -244,7 +242,7 @@ impl SessionFileStorage { return Ok(files); } - let entries = fs::read_dir(&files_dir).map_err(|e| format!("读取文件目录失败: {}", e))?; + let entries = fs::read_dir(&files_dir).map_err(|e| format!("读取文件目录失败: {e}"))?; for entry in entries.flatten() { if entry.path().is_file() { @@ -306,11 +304,9 @@ impl SessionFileStorage { let sessions = self.list_sessions()?; for session in sessions { - if session.updated_at < cutoff { - if self.delete_session(&session.session_id).is_ok() { - cleaned += 1; - tracing::info!("[SessionFileStorage] 清理过期会话: {}", session.session_id); - } + if session.updated_at < cutoff && self.delete_session(&session.session_id).is_ok() { + cleaned += 1; + tracing::info!("[SessionFileStorage] 清理过期会话: {}", session.session_id); } } @@ -323,10 +319,8 @@ impl SessionFileStorage { let sessions = self.list_sessions()?; for session in sessions { - if session.file_count == 0 { - if self.delete_session(&session.session_id).is_ok() { - cleaned += 1; - } + if session.file_count == 0 && self.delete_session(&session.session_id).is_ok() { + cleaned += 1; } } diff --git a/src-tauri/src/stream/generators/anthropic_sse.rs b/src-tauri/src/stream/generators/anthropic_sse.rs index d2dc97011..1a294beca 100644 --- a/src-tauri/src/stream/generators/anthropic_sse.rs +++ b/src-tauri/src/stream/generators/anthropic_sse.rs @@ -113,12 +113,9 @@ impl AnthropicSseGenerator { // 确保发送 message_start if !self.message_started { - match event { - StreamEvent::MessageStart { id, model } => { - self.message_id = id.clone(); - self.model = model.clone(); - } - _ => {} + if let StreamEvent::MessageStart { id, model } = event { + self.message_id = id.clone(); + self.model = model.clone(); } sse_events.push(self.create_message_start()); self.message_started = true; @@ -266,7 +263,7 @@ impl AnthropicSseGenerator { } } }); - format!("event: message_start\ndata: {}\n\n", event) + format!("event: message_start\ndata: {event}\n\n") } fn create_content_block_start_text(&self, index: u32) -> String { @@ -278,7 +275,7 @@ impl AnthropicSseGenerator { "text": "" } }); - format!("event: content_block_start\ndata: {}\n\n", event) + format!("event: content_block_start\ndata: {event}\n\n") } fn create_content_block_start_tool(&self, index: u32, id: &str, name: &str) -> String { @@ -292,7 +289,7 @@ impl AnthropicSseGenerator { "input": {} } }); - format!("event: content_block_start\ndata: {}\n\n", event) + format!("event: content_block_start\ndata: {event}\n\n") } fn create_text_delta(&self, index: u32, text: &str) -> String { @@ -304,7 +301,7 @@ impl AnthropicSseGenerator { "text": text } }); - format!("event: content_block_delta\ndata: {}\n\n", event) + format!("event: content_block_delta\ndata: {event}\n\n") } fn create_input_json_delta(&self, index: u32, partial_json: &str) -> String { @@ -316,7 +313,7 @@ impl AnthropicSseGenerator { "partial_json": partial_json } }); - format!("event: content_block_delta\ndata: {}\n\n", event) + format!("event: content_block_delta\ndata: {event}\n\n") } fn create_content_block_stop(&self, index: u32) -> String { @@ -324,7 +321,7 @@ impl AnthropicSseGenerator { "type": "content_block_stop", "index": index }); - format!("event: content_block_stop\ndata: {}\n\n", event) + format!("event: content_block_stop\ndata: {event}\n\n") } fn create_message_delta(&self, stop_reason: &StopReason) -> String { @@ -338,21 +335,21 @@ impl AnthropicSseGenerator { "output_tokens": self.output_tokens } }); - format!("event: message_delta\ndata: {}\n\n", event) + format!("event: message_delta\ndata: {event}\n\n") } fn create_message_stop(&self) -> String { let event = serde_json::json!({ "type": "message_stop" }); - format!("event: message_stop\ndata: {}\n\n", event) + format!("event: message_stop\ndata: {event}\n\n") } fn create_ping(&self) -> String { let event = serde_json::json!({ "type": "ping" }); - format!("event: ping\ndata: {}\n\n", event) + format!("event: ping\ndata: {event}\n\n") } fn create_error(&self, error_type: &str, message: &str) -> String { @@ -363,7 +360,7 @@ impl AnthropicSseGenerator { "message": message } }); - format!("event: error\ndata: {}\n\n", event) + format!("event: error\ndata: {event}\n\n") } } diff --git a/src-tauri/src/stream/generators/openai_sse.rs b/src-tauri/src/stream/generators/openai_sse.rs index f7e049b0a..eced991f2 100644 --- a/src-tauri/src/stream/generators/openai_sse.rs +++ b/src-tauri/src/stream/generators/openai_sse.rs @@ -242,7 +242,7 @@ impl OpenAiSseGenerator { }; let chunk_str = format!("data: {}\n\n", serde_json::to_string(&chunk).ok()?); - Some(format!("{}data: [DONE]\n\n", chunk_str)) + Some(format!("{chunk_str}data: [DONE]\n\n")) } StreamEvent::Usage { @@ -272,7 +272,7 @@ impl OpenAiSseGenerator { "message": message, } }); - Some(format!("data: {}\n\n", error_obj)) + Some(format!("data: {error_obj}\n\n")) } StreamEvent::Ping => { diff --git a/src-tauri/src/stream/parsers/aws_event_stream.rs b/src-tauri/src/stream/parsers/aws_event_stream.rs index b1e97f8dc..7a0e582ab 100644 --- a/src-tauri/src/stream/parsers/aws_event_stream.rs +++ b/src-tauri/src/stream/parsers/aws_event_stream.rs @@ -381,7 +381,7 @@ impl AwsEventStreamParser { ); let value: serde_json::Value = - serde_json::from_str(json_str).map_err(|e| format!("JSON 解析错误: {}", e))?; + serde_json::from_str(json_str).map_err(|e| format!("JSON 解析错误: {e}"))?; let mut events = Vec::new(); diff --git a/src-tauri/src/streaming/anthropic_sse.rs b/src-tauri/src/streaming/anthropic_sse.rs index d5b608ab1..80db4e2f8 100644 --- a/src-tauri/src/streaming/anthropic_sse.rs +++ b/src-tauri/src/streaming/anthropic_sse.rs @@ -327,7 +327,7 @@ impl AnthropicSseGenerator { } } }); - format!("event: message_start\ndata: {}\n\n", event) + format!("event: message_start\ndata: {event}\n\n") } /// 创建文本类型的 content_block_start 事件 @@ -340,7 +340,7 @@ impl AnthropicSseGenerator { "text": "" } }); - format!("event: content_block_start\ndata: {}\n\n", event) + format!("event: content_block_start\ndata: {event}\n\n") } /// 创建工具调用类型的 content_block_start 事件 @@ -355,7 +355,7 @@ impl AnthropicSseGenerator { "input": {} } }); - format!("event: content_block_start\ndata: {}\n\n", event) + format!("event: content_block_start\ndata: {event}\n\n") } /// 创建文本增量事件 @@ -368,7 +368,7 @@ impl AnthropicSseGenerator { "text": text } }); - format!("event: content_block_delta\ndata: {}\n\n", event) + format!("event: content_block_delta\ndata: {event}\n\n") } /// 创建工具调用输入增量事件 @@ -381,7 +381,7 @@ impl AnthropicSseGenerator { "partial_json": partial_json } }); - format!("event: content_block_delta\ndata: {}\n\n", event) + format!("event: content_block_delta\ndata: {event}\n\n") } /// 创建 content_block_stop 事件 @@ -390,7 +390,7 @@ impl AnthropicSseGenerator { "type": "content_block_stop", "index": index }); - format!("event: content_block_stop\ndata: {}\n\n", event) + format!("event: content_block_stop\ndata: {event}\n\n") } /// 创建 message_delta 事件 @@ -411,7 +411,7 @@ impl AnthropicSseGenerator { "output_tokens": self.output_tokens } }); - format!("event: message_delta\ndata: {}\n\n", event) + format!("event: message_delta\ndata: {event}\n\n") } /// 创建 message_stop 事件 @@ -419,7 +419,7 @@ impl AnthropicSseGenerator { let event = serde_json::json!({ "type": "message_stop" }); - format!("event: message_stop\ndata: {}\n\n", event) + format!("event: message_stop\ndata: {event}\n\n") } } diff --git a/src-tauri/src/streaming/aws_parser.rs b/src-tauri/src/streaming/aws_parser.rs index 8b261d648..45c386eb3 100644 --- a/src-tauri/src/streaming/aws_parser.rs +++ b/src-tauri/src/streaming/aws_parser.rs @@ -379,7 +379,7 @@ impl AwsEventStreamParser { /// 解析 JSON 事件 fn parse_json_event(&mut self, json_str: &str) -> Result, String> { let value: serde_json::Value = - serde_json::from_str(json_str).map_err(|e| format!("JSON 解析错误: {}", e))?; + serde_json::from_str(json_str).map_err(|e| format!("JSON 解析错误: {e}"))?; let mut events = Vec::new(); @@ -1634,8 +1634,8 @@ mod property_tests { let mut parser = AwsEventStreamParser::new(); // 构造数据:无效 JSON + 有效 JSON - let valid_json = format!(r#"{{"content":"{}"}}"#, valid_text); - let data = format!("{}{}", invalid_prefix, valid_json); + let valid_json = format!(r#"{{"content":"{valid_text}"}}"#); + let data = format!("{invalid_prefix}{valid_json}"); // 解析 let events = parser.process(data.as_bytes()); @@ -1651,7 +1651,7 @@ mod property_tests { .collect(); prop_assert!( - parse_errors.len() >= 1, + !parse_errors.is_empty(), "应该至少有一个解析错误" ); prop_assert_eq!( @@ -1680,9 +1680,9 @@ mod property_tests { let mut data = String::new(); for (i, text) in valid_texts.iter().enumerate() { // 添加无效 JSON - data.push_str(&format!("{{invalid{}}}", i)); + data.push_str(&format!("{{invalid{i}}}")); // 添加有效 JSON - data.push_str(&format!(r#"{{"content":"{}"}}"#, text)); + data.push_str(&format!(r#"{{"content":"{text}"}}"#)); } // 解析 @@ -1725,7 +1725,7 @@ mod property_tests { // 发送多个无效 JSON for i in 0..num_invalid { - parser.process(format!("{{invalid{}}}", i).as_bytes()); + parser.process(format!("{{invalid{i}}}").as_bytes()); } // 验证错误计数 @@ -1736,7 +1736,7 @@ mod property_tests { ); // 发送有效 JSON - let valid_json = format!(r#"{{"content":"{}"}}"#, valid_text); + let valid_json = format!(r#"{{"content":"{valid_text}"}}"#); let events = parser.process(valid_json.as_bytes()); // 验证:有效 JSON 不应增加错误计数 @@ -1767,7 +1767,7 @@ mod property_tests { // 构造数据:二进制垃圾 + 有效 JSON let mut data = vec![0xFF; garbage_len]; - let valid_json = format!(r#"{{"content":"{}"}}"#, valid_text); + let valid_json = format!(r#"{{"content":"{valid_text}"}}"#); data.extend_from_slice(valid_json.as_bytes()); // 解析 diff --git a/src-tauri/src/streaming/converter.rs b/src-tauri/src/streaming/converter.rs index 1796183e9..723804ef8 100644 --- a/src-tauri/src/streaming/converter.rs +++ b/src-tauri/src/streaming/converter.rs @@ -625,7 +625,7 @@ impl StreamConverter { } } }); - format!("event: message_start\ndata: {}\n\n", event) + format!("event: message_start\ndata: {event}\n\n") } fn create_anthropic_content_block_start_text(&self, index: u32) -> String { @@ -637,7 +637,7 @@ impl StreamConverter { "text": "" } }); - format!("event: content_block_start\ndata: {}\n\n", event) + format!("event: content_block_start\ndata: {event}\n\n") } fn create_anthropic_content_block_start_tool( @@ -656,7 +656,7 @@ impl StreamConverter { "input": {} } }); - format!("event: content_block_start\ndata: {}\n\n", event) + format!("event: content_block_start\ndata: {event}\n\n") } fn create_anthropic_text_delta(&self, index: u32, text: &str) -> String { @@ -668,7 +668,7 @@ impl StreamConverter { "text": text } }); - format!("event: content_block_delta\ndata: {}\n\n", event) + format!("event: content_block_delta\ndata: {event}\n\n") } fn create_anthropic_input_json_delta(&self, index: u32, partial_json: &str) -> String { @@ -680,7 +680,7 @@ impl StreamConverter { "partial_json": partial_json } }); - format!("event: content_block_delta\ndata: {}\n\n", event) + format!("event: content_block_delta\ndata: {event}\n\n") } fn create_anthropic_content_block_stop(&self, index: u32) -> String { @@ -688,7 +688,7 @@ impl StreamConverter { "type": "content_block_stop", "index": index }); - format!("event: content_block_stop\ndata: {}\n\n", event) + format!("event: content_block_stop\ndata: {event}\n\n") } fn create_anthropic_message_delta(&self) -> String { @@ -702,14 +702,14 @@ impl StreamConverter { "output_tokens": 0 } }); - format!("event: message_delta\ndata: {}\n\n", event) + format!("event: message_delta\ndata: {event}\n\n") } fn create_anthropic_message_stop(&self) -> String { let event = serde_json::json!({ "type": "message_stop" }); - format!("event: message_stop\ndata: {}\n\n", event) + format!("event: message_stop\ndata: {event}\n\n") } // ======================================================================== @@ -738,7 +738,7 @@ impl StreamConverter { "finish_reason": null }] }); - format!("data: {}\n\n", chunk) + format!("data: {chunk}\n\n") } fn create_openai_tool_call_chunk( @@ -781,7 +781,7 @@ impl StreamConverter { "finish_reason": null }] }); - format!("data: {}\n\n", chunk) + format!("data: {chunk}\n\n") } fn create_openai_finish_chunk(&self, finish_reason: &str) -> String { @@ -796,7 +796,7 @@ impl StreamConverter { "finish_reason": finish_reason }] }); - format!("data: {}\n\n", chunk) + format!("data: {chunk}\n\n") } } @@ -1376,7 +1376,7 @@ mod property_tests { value in "[a-zA-Z0-9]{1,20}", split_points in prop::collection::vec(1usize..50, 1..5) ) { - let json = format!("{{\"{}\":\"{}\"}}", key, value); + let json = format!("{{\"{key}\":\"{value}\"}}"); let bytes = json.as_bytes(); let mut acc = PartialJsonAccumulator::new(); @@ -1423,7 +1423,7 @@ mod property_tests { value in "[a-zA-Z0-9]{1,10}", chunk_size in 1usize..10 ) { - let json = format!("{{\"{}\":{{\"{}\":\"{}\"}}}}", outer_key, inner_key, value); + let json = format!("{{\"{outer_key}\":{{\"{inner_key}\":\"{value}\"}}}}"); let bytes = json.as_bytes(); let mut acc = PartialJsonAccumulator::new(); @@ -1459,7 +1459,7 @@ mod property_tests { chunk_size in 1usize..15 ) { // 创建包含括号的字符串值 - let json = format!("{{\"{}\":\"{}{{}}[]{}\"}}", key, prefix, suffix); + let json = format!("{{\"{key}\":\"{prefix}{{}}[]{suffix}\"}}"); let bytes = json.as_bytes(); let mut acc = PartialJsonAccumulator::new(); @@ -1494,8 +1494,8 @@ mod property_tests { key2 in "[a-z]{1,5}", value2 in "[a-zA-Z0-9]{1,10}" ) { - let json1 = format!("{{\"{}\":\"{}\"}}", key1, value1); - let json2 = format!("{{\"{}\":\"{}\"}}", key2, value2); + let json1 = format!("{{\"{key1}\":\"{value1}\"}}"); + let json2 = format!("{{\"{key2}\":\"{value2}\"}}"); let mut acc = PartialJsonAccumulator::new(); diff --git a/src-tauri/src/streaming/error.rs b/src-tauri/src/streaming/error.rs index fad3bf3f2..4bf9abda7 100644 --- a/src-tauri/src/streaming/error.rs +++ b/src-tauri/src/streaming/error.rs @@ -64,15 +64,15 @@ pub enum StreamError { impl fmt::Display for StreamError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - StreamError::Network(msg) => write!(f, "网络错误: {}", msg), + StreamError::Network(msg) => write!(f, "网络错误: {msg}"), StreamError::Timeout => write!(f, "流式响应超时"), - StreamError::ParseError(msg) => write!(f, "解析错误: {}", msg), + StreamError::ParseError(msg) => write!(f, "解析错误: {msg}"), StreamError::ProviderError { status, message } => { - write!(f, "Provider 错误 ({}): {}", status, message) + write!(f, "Provider 错误 ({status}): {message}") } StreamError::ClientDisconnected => write!(f, "客户端已断开连接"), StreamError::BufferOverflow => write!(f, "缓冲区溢出"), - StreamError::Internal(msg) => write!(f, "内部错误: {}", msg), + StreamError::Internal(msg) => write!(f, "内部错误: {msg}"), } } } @@ -94,9 +94,9 @@ impl From for StreamError { if err.is_timeout() { StreamError::Timeout } else if err.is_connect() { - StreamError::Network(format!("连接失败: {}", err)) + StreamError::Network(format!("连接失败: {err}")) } else if err.is_request() { - StreamError::Network(format!("请求错误: {}", err)) + StreamError::Network(format!("请求错误: {err}")) } else { StreamError::Network(err.to_string()) } @@ -187,7 +187,7 @@ impl StreamError { "message": self.to_string(), } }); - format!("event: error\ndata: {}\n\n", error_json) + format!("event: error\ndata: {error_json}\n\n") } /// 获取错误类型字符串 diff --git a/src-tauri/src/streaming/manager.rs b/src-tauri/src/streaming/manager.rs index 38df5fb25..875688a6c 100644 --- a/src-tauri/src/streaming/manager.rs +++ b/src-tauri/src/streaming/manager.rs @@ -1602,7 +1602,7 @@ mod property_tests { // 将事件序列化为字节流 let chunks: Vec> = events .iter() - .filter_map(|event| serialize_event(event)) + .filter_map(serialize_event) .map(|json| Ok(Bytes::from(json))) .collect(); @@ -1647,8 +1647,7 @@ mod property_tests { // 验证内容一致 if original_content != converted_content { return Err(format!( - "Flow Monitor 捕获的内容应该与原始内容一致: original={}, converted={}", - original_content, converted_content + "Flow Monitor 捕获的内容应该与原始内容一致: original={original_content}, converted={converted_content}" )); } @@ -1676,7 +1675,7 @@ mod property_tests { // 将事件序列化为字节流 let chunks: Vec> = events .iter() - .filter_map(|event| serialize_event(event)) + .filter_map(serialize_event) .map(|json| Ok(Bytes::from(json))) .collect(); @@ -1739,7 +1738,7 @@ mod property_tests { // 将事件序列化为字节流 let chunks: Vec> = events .iter() - .filter_map(|event| serialize_event(event)) + .filter_map(serialize_event) .map(|json| Ok(Bytes::from(json))) .collect(); @@ -1819,7 +1818,7 @@ mod property_tests { // 创建包含有效事件和错误的流 let mut chunks: Vec> = valid_events .iter() - .filter_map(|event| serialize_event(event)) + .filter_map(serialize_event) .map(|json| Ok(Bytes::from(json))) .collect(); diff --git a/src-tauri/src/streaming/metrics.rs b/src-tauri/src/streaming/metrics.rs index bb260f0ba..529a4c0de 100644 --- a/src-tauri/src/streaming/metrics.rs +++ b/src-tauri/src/streaming/metrics.rs @@ -270,7 +270,7 @@ impl StreamMetrics { let duration = self.duration_ms(); let ttfb = self .ttfb_ms - .map(|t| format!("{}ms", t)) + .map(|t| format!("{t}ms")) .unwrap_or_else(|| "N/A".to_string()); let throughput = self .throughput_bytes_per_sec() @@ -278,7 +278,7 @@ impl StreamMetrics { .unwrap_or_else(|| "N/A".to_string()); let avg_chunk = self .avg_chunk_size() - .map(|s| format!("{:.0}B", s)) + .map(|s| format!("{s:.0}B")) .unwrap_or_else(|| "N/A".to_string()); format!( diff --git a/src-tauri/src/streaming/traits.rs b/src-tauri/src/streaming/traits.rs index 58369569a..f1db186fa 100644 --- a/src-tauri/src/streaming/traits.rs +++ b/src-tauri/src/streaming/traits.rs @@ -130,7 +130,7 @@ pub fn reqwest_stream_to_stream_response(response: reqwest::Response) -> StreamR let stream = response .bytes_stream() - .map(|result| result.map_err(|e| StreamError::from(e))); + .map(|result| result.map_err(StreamError::from)); Box::pin(stream) } diff --git a/src-tauri/src/terminal/block_controller/shell_controller.rs b/src-tauri/src/terminal/block_controller/shell_controller.rs index 6cb05f5b4..e38d8a3e8 100644 --- a/src-tauri/src/terminal/block_controller/shell_controller.rs +++ b/src-tauri/src/terminal/block_controller/shell_controller.rs @@ -278,16 +278,15 @@ impl ShellController { /// _Requirements: 16.6_ fn should_run(&self, block_meta: &BlockMeta) -> bool { // 如果是 cmd 模式且设置了 runonce,检查是否已运行过 - if self.controller_type == "cmd" { - if block_meta.cmd_run_once.unwrap_or(false) { - if self.has_run.load(Ordering::SeqCst) { - tracing::debug!( - "[ShellController] cmd:runonce 已运行过,跳过: block_id={}", - self.block_id - ); - return false; - } - } + if self.controller_type == "cmd" + && block_meta.cmd_run_once.unwrap_or(false) + && self.has_run.load(Ordering::SeqCst) + { + tracing::debug!( + "[ShellController] cmd:runonce 已运行过,跳过: block_id={}", + self.block_id + ); + return false; } true } @@ -575,7 +574,7 @@ impl BlockController for ShellController { sender .send(input.clone()) .await - .map_err(|e| TerminalError::WriteFailed(format!("发送输入失败: {}", e)))?; + .map_err(|e| TerminalError::WriteFailed(format!("发送输入失败: {e}")))?; Ok(()) } else { Err(TerminalError::SessionClosed) diff --git a/src-tauri/src/terminal/block_controller/traits.rs b/src-tauri/src/terminal/block_controller/traits.rs index 4ca1f08de..138f2db45 100644 --- a/src-tauri/src/terminal/block_controller/traits.rs +++ b/src-tauri/src/terminal/block_controller/traits.rs @@ -170,20 +170,12 @@ impl BlockMeta { } /// 运行时选项 -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct RuntimeOpts { /// 终端大小 pub term_size: TermSize, } -impl Default for RuntimeOpts { - fn default() -> Self { - Self { - term_size: TermSize::default(), - } - } -} - /// 块控制器 trait /// /// 所有控制器类型(Shell、Cmd、SSH、WSL)都必须实现此 trait。 diff --git a/src-tauri/src/terminal/connections/connection_config.rs b/src-tauri/src/terminal/connections/connection_config.rs index 995f5a06f..ba43a1aed 100644 --- a/src-tauri/src/terminal/connections/connection_config.rs +++ b/src-tauri/src/terminal/connections/connection_config.rs @@ -224,10 +224,10 @@ impl ConnectionConfigManager { return Ok(ConnectionsFile::new()); } - let content = fs::read_to_string(&self.config_path) - .map_err(|e| format!("读取配置文件失败: {}", e))?; + let content = + fs::read_to_string(&self.config_path).map_err(|e| format!("读取配置文件失败: {e}"))?; - serde_json::from_str(&content).map_err(|e| format!("解析配置文件失败: {}", e)) + serde_json::from_str(&content).map_err(|e| format!("解析配置文件失败: {e}")) } /// 保存连接配置 @@ -235,14 +235,14 @@ impl ConnectionConfigManager { // 确保父目录存在 if let Some(parent) = self.config_path.parent() { if !parent.exists() { - fs::create_dir_all(parent).map_err(|e| format!("创建配置目录失败: {}", e))?; + fs::create_dir_all(parent).map_err(|e| format!("创建配置目录失败: {e}"))?; } } let content = - serde_json::to_string_pretty(config).map_err(|e| format!("序列化配置失败: {}", e))?; + serde_json::to_string_pretty(config).map_err(|e| format!("序列化配置失败: {e}"))?; - fs::write(&self.config_path, content).map_err(|e| format!("写入配置文件失败: {}", e))?; + fs::write(&self.config_path, content).map_err(|e| format!("写入配置文件失败: {e}"))?; tracing::info!("[ConnectionConfig] 配置已保存: {:?}", self.config_path); Ok(()) @@ -252,16 +252,16 @@ impl ConnectionConfigManager { pub fn save_raw(&self, content: &str) -> Result<(), String> { // 先验证 JSON 格式 let _: ConnectionsFile = - serde_json::from_str(content).map_err(|e| format!("无效的 JSON 格式: {}", e))?; + serde_json::from_str(content).map_err(|e| format!("无效的 JSON 格式: {e}"))?; // 确保父目录存在 if let Some(parent) = self.config_path.parent() { if !parent.exists() { - fs::create_dir_all(parent).map_err(|e| format!("创建配置目录失败: {}", e))?; + fs::create_dir_all(parent).map_err(|e| format!("创建配置目录失败: {e}"))?; } } - fs::write(&self.config_path, content).map_err(|e| format!("写入配置文件失败: {}", e))?; + fs::write(&self.config_path, content).map_err(|e| format!("写入配置文件失败: {e}"))?; tracing::info!("[ConnectionConfig] 原始配置已保存: {:?}", self.config_path); Ok(()) @@ -277,7 +277,7 @@ impl ConnectionConfigManager { .to_string()); } - fs::read_to_string(&self.config_path).map_err(|e| format!("读取配置文件失败: {}", e)) + fs::read_to_string(&self.config_path).map_err(|e| format!("读取配置文件失败: {e}")) } /// 从系统 SSH 配置读取 Host 列表 @@ -384,7 +384,7 @@ impl ConnectionConfigManager { // 获取本地系统信息 let local_user = whoami::username(); let local_host = whoami::fallible::hostname().unwrap_or_else(|_| "localhost".to_string()); - let local_label = format!("{}@{}", local_user, local_host); + let local_label = format!("{local_user}@{local_host}"); // 添加本地连接 entries.push(ConnectionListEntry { @@ -410,14 +410,14 @@ impl ConnectionConfigManager { let host = conn.host.as_deref().unwrap_or("unknown"); let port = conn.port.unwrap_or(22); if port == 22 { - format!("{}@{}", user, host) + format!("{user}@{host}") } else { - format!("{}@{}:{}", user, host, port) + format!("{user}@{host}:{port}") } } ConnectionConfigType::Wsl => { let distro = conn.wsl_distro.as_deref().unwrap_or("default"); - format!("WSL: {}", distro) + format!("WSL: {distro}") } ConnectionConfigType::Local => "Local".to_string(), }; @@ -444,7 +444,7 @@ impl ConnectionConfigManager { let label = if let Some(ref user) = host.user { if let Some(ref hostname) = host.hostname { - format!("{}@{}", user, hostname) + format!("{user}@{hostname}") } else { format!("{}@{}", user, host.pattern) } diff --git a/src-tauri/src/terminal/connections/local_pty.rs b/src-tauri/src/terminal/connections/local_pty.rs index 7eaa1493d..032fe0844 100644 --- a/src-tauri/src/terminal/connections/local_pty.rs +++ b/src-tauri/src/terminal/connections/local_pty.rs @@ -224,7 +224,7 @@ impl ShellProc { let app_data_dir = app_handle .path() .app_data_dir() - .map_err(|e| TerminalError::Internal(format!("获取应用数据目录失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("获取应用数据目录失败: {e}")))?; // 使用 ShellLaunchBuilder 构建启动配置 let builder = ShellLaunchBuilder::new(&app_data_dir, block_id.to_string()); diff --git a/src-tauri/src/terminal/connections/ssh_connection.rs b/src-tauri/src/terminal/connections/ssh_connection.rs index 4da56a9d2..5a51e282b 100644 --- a/src-tauri/src/terminal/connections/ssh_connection.rs +++ b/src-tauri/src/terminal/connections/ssh_connection.rs @@ -149,23 +149,20 @@ impl SSHOpts { let host = &host_port[1..bracket_end]; let remaining = &host_port[bracket_end + 1..]; - let port = if remaining.starts_with(':') { - let port_str = &remaining[1..]; + let port = if let Some(port_str) = remaining.strip_prefix(':') { Some(Self::parse_port(port_str)?) } else if remaining.is_empty() { None } else { return Err(TerminalError::SSHConnectionFailed(format!( - "无效的 IPv6 地址格式: {}", - host_port + "无效的 IPv6 地址格式: {host_port}" ))); }; return Ok((host.to_string(), port)); } else { return Err(TerminalError::SSHConnectionFailed(format!( - "无效的 IPv6 地址格式,缺少 ']': {}", - host_port + "无效的 IPv6 地址格式,缺少 ']': {host_port}" ))); } } @@ -192,7 +189,7 @@ impl SSHOpts { fn parse_port(port_str: &str) -> Result { port_str .parse::() - .map_err(|_| TerminalError::SSHConnectionFailed(format!("无效的端口号: {}", port_str))) + .map_err(|_| TerminalError::SSHConnectionFailed(format!("无效的端口号: {port_str}"))) } /// 获取有效端口(如果未指定则返回默认端口) @@ -706,8 +703,7 @@ impl SSHConn { let current_state = self.state(); if !current_state.can_transition_to(ConnectionState::Connecting) { return Err(TerminalError::SSHConnectionFailed(format!( - "无法从 {} 状态开始连接", - current_state + "无法从 {current_state} 状态开始连接" ))); } @@ -723,7 +719,7 @@ impl SSHConn { let tcp = match TcpStream::connect(&addr) { Ok(stream) => stream, Err(e) => { - let error_msg = format!("TCP 连接失败: {}", e); + let error_msg = format!("TCP 连接失败: {e}"); tracing::error!("[SSHConn] {}", error_msg); self.set_state(ConnectionState::Error); self.set_error(Some(error_msg.clone())); @@ -734,7 +730,7 @@ impl SSHConn { // 创建 SSH 会话 let mut session = Session::new().map_err(|e| { - let error_msg = format!("创建 SSH 会话失败: {}", e); + let error_msg = format!("创建 SSH 会话失败: {e}"); self.set_state(ConnectionState::Error); self.set_error(Some(error_msg.clone())); self.broadcast_conn_change(); @@ -743,7 +739,7 @@ impl SSHConn { // 设置 TCP 流 session.set_tcp_stream(tcp.try_clone().map_err(|e| { - let error_msg = format!("克隆 TCP 流失败: {}", e); + let error_msg = format!("克隆 TCP 流失败: {e}"); self.set_state(ConnectionState::Error); self.set_error(Some(error_msg.clone())); self.broadcast_conn_change(); @@ -752,7 +748,7 @@ impl SSHConn { // 执行 SSH 握手 session.handshake().map_err(|e| { - let error_msg = format!("SSH 握手失败: {}", e); + let error_msg = format!("SSH 握手失败: {e}"); tracing::error!("[SSHConn] {}", error_msg); self.set_state(ConnectionState::Error); self.set_error(Some(error_msg.clone())); @@ -836,18 +832,18 @@ impl SSHConn { SSHAuthMethod::Agent => { tracing::debug!("[SSHConn] 尝试 SSH Agent 认证"); let mut agent = session.agent().map_err(|e| { - TerminalError::SSHAuthFailed(format!("获取 SSH Agent 失败: {}", e)) + TerminalError::SSHAuthFailed(format!("获取 SSH Agent 失败: {e}")) })?; agent.connect().map_err(|e| { - TerminalError::SSHAuthFailed(format!("连接 SSH Agent 失败: {}", e)) + TerminalError::SSHAuthFailed(format!("连接 SSH Agent 失败: {e}")) })?; agent .list_identities() - .map_err(|e| TerminalError::SSHAuthFailed(format!("列出身份失败: {}", e)))?; + .map_err(|e| TerminalError::SSHAuthFailed(format!("列出身份失败: {e}")))?; - let identities: Vec<_> = agent.identities().map_err(|e| { - TerminalError::SSHAuthFailed(format!("获取身份列表失败: {}", e)) - })?; + let identities: Vec<_> = agent + .identities() + .map_err(|e| TerminalError::SSHAuthFailed(format!("获取身份列表失败: {e}")))?; for identity in identities { if agent.userauth(username, &identity).is_ok() { @@ -943,7 +939,7 @@ impl SSHConn { // 转换为 Base64 格式的指纹 use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; - format!("SHA256:{}", BASE64.encode(&result)) + format!("SHA256:{}", BASE64.encode(result)) } /// 检查 known_hosts 文件 @@ -1001,7 +997,7 @@ impl SSHConn { })?; let mut known_hosts = session.known_hosts().map_err(|e| { - TerminalError::HostKeyVerificationFailed(format!("获取 known_hosts 失败: {}", e)) + TerminalError::HostKeyVerificationFailed(format!("获取 known_hosts 失败: {e}")) })?; // 读取现有的 known_hosts 文件 @@ -1038,14 +1034,14 @@ impl SSHConn { known_hosts .add(&host_with_port, host_key, "", key_type) .map_err(|e| { - TerminalError::HostKeyVerificationFailed(format!("添加主机密钥失败: {}", e)) + TerminalError::HostKeyVerificationFailed(format!("添加主机密钥失败: {e}")) })?; // 确保 .ssh 目录存在 if let Some(ssh_dir) = known_hosts_path.parent() { if !ssh_dir.exists() { std::fs::create_dir_all(ssh_dir).map_err(|e| { - TerminalError::HostKeyVerificationFailed(format!("创建 .ssh 目录失败: {}", e)) + TerminalError::HostKeyVerificationFailed(format!("创建 .ssh 目录失败: {e}")) })?; } } @@ -1054,7 +1050,7 @@ impl SSHConn { known_hosts .write_file(&known_hosts_path, ssh2::KnownHostFileKind::OpenSSH) .map_err(|e| { - TerminalError::HostKeyVerificationFailed(format!("写入 known_hosts 失败: {}", e)) + TerminalError::HostKeyVerificationFailed(format!("写入 known_hosts 失败: {e}")) })?; tracing::info!("[SSHConn] 已添加主机密钥到 known_hosts: {}", host_with_port); @@ -1192,18 +1188,18 @@ impl SSHConn { SSHAuthMethod::Agent => { tracing::debug!("[SSHConn] 尝试 SSH Agent 认证"); let mut agent = session.agent().map_err(|e| { - TerminalError::SSHAuthFailed(format!("获取 SSH Agent 失败: {}", e)) + TerminalError::SSHAuthFailed(format!("获取 SSH Agent 失败: {e}")) })?; agent.connect().map_err(|e| { - TerminalError::SSHAuthFailed(format!("连接 SSH Agent 失败: {}", e)) + TerminalError::SSHAuthFailed(format!("连接 SSH Agent 失败: {e}")) })?; agent .list_identities() - .map_err(|e| TerminalError::SSHAuthFailed(format!("列出身份失败: {}", e)))?; + .map_err(|e| TerminalError::SSHAuthFailed(format!("列出身份失败: {e}")))?; - let identities: Vec<_> = agent.identities().map_err(|e| { - TerminalError::SSHAuthFailed(format!("获取身份列表失败: {}", e)) - })?; + let identities: Vec<_> = agent + .identities() + .map_err(|e| TerminalError::SSHAuthFailed(format!("获取身份列表失败: {e}")))?; for identity in identities { if agent.userauth(username, &identity).is_ok() && session.authenticated() { @@ -1373,7 +1369,7 @@ impl SSHConfigParser { /// _Requirements: 4.12_ pub fn parse_config(path: &PathBuf) -> Result, TerminalError> { let content = std::fs::read_to_string(path).map_err(|e| { - TerminalError::SSHConnectionFailed(format!("读取 SSH 配置文件失败: {}", e)) + TerminalError::SSHConnectionFailed(format!("读取 SSH 配置文件失败: {e}")) })?; Self::parse_config_content(&content) @@ -1486,12 +1482,11 @@ impl SSHConfigParser { /// 移除值两端的引号 fn unquote(value: &str) -> String { let value = value.trim(); - if (value.starts_with('"') && value.ends_with('"')) - || (value.starts_with('\'') && value.ends_with('\'')) + if ((value.starts_with('"') && value.ends_with('"')) + || (value.starts_with('\'') && value.ends_with('\''))) + && value.len() >= 2 { - if value.len() >= 2 { - return value[1..value.len() - 1].to_string(); - } + return value[1..value.len() - 1].to_string(); } value.to_string() } @@ -1635,7 +1630,7 @@ impl SSHConfigParser { host: &str, ) -> Result { let content = std::fs::read_to_string(path).map_err(|e| { - TerminalError::SSHConnectionFailed(format!("读取 SSH 配置文件失败: {}", e)) + TerminalError::SSHConnectionFailed(format!("读取 SSH 配置文件失败: {e}")) })?; Self::get_host_config_from_content(&content, host) @@ -1886,8 +1881,7 @@ impl SSHConfigParser { ) -> Result, TerminalError> { if depth > MAX_PROXY_JUMP_DEPTH { return Err(TerminalError::SSHConnectionFailed(format!( - "ProxyJump 链深度超过最大限制 {}", - MAX_PROXY_JUMP_DEPTH + "ProxyJump 链深度超过最大限制 {MAX_PROXY_JUMP_DEPTH}" ))); } diff --git a/src-tauri/src/terminal/connections/ssh_shell_proc.rs b/src-tauri/src/terminal/connections/ssh_shell_proc.rs index 8eab7df78..b9b210113 100644 --- a/src-tauri/src/terminal/connections/ssh_shell_proc.rs +++ b/src-tauri/src/terminal/connections/ssh_shell_proc.rs @@ -95,7 +95,7 @@ impl SSHShellProc { // 创建 SSH Channel let mut channel = session.channel_session().map_err(|e| { - TerminalError::SSHConnectionFailed(format!("创建 SSH Channel 失败: {}", e)) + TerminalError::SSHConnectionFailed(format!("创建 SSH Channel 失败: {e}")) })?; // 请求 PTY @@ -106,7 +106,7 @@ impl SSHShellProc { None, Some((cols as u32, rows as u32, 0, 0)), ) - .map_err(|e| TerminalError::SSHConnectionFailed(format!("请求远程 PTY 失败: {}", e)))?; + .map_err(|e| TerminalError::SSHConnectionFailed(format!("请求远程 PTY 失败: {e}")))?; // 根据控制器类型启动 Shell 或执行命令 if controller_type == "cmd" { @@ -114,12 +114,12 @@ impl SSHShellProc { let cmd = Self::build_remote_command(&block_meta)?; tracing::info!("[SSHShellProc] 执行远程命令: {}", cmd); channel.exec(&cmd).map_err(|e| { - TerminalError::SSHConnectionFailed(format!("执行远程命令失败: {}", e)) + TerminalError::SSHConnectionFailed(format!("执行远程命令失败: {e}")) })?; } else { // Shell 模式 - 启动交互式 Shell channel.shell().map_err(|e| { - TerminalError::SSHConnectionFailed(format!("启动远程 Shell 失败: {}", e)) + TerminalError::SSHConnectionFailed(format!("启动远程 Shell 失败: {e}")) })?; } @@ -636,7 +636,7 @@ impl SSHShellProc { let mut channel = self.channel.lock(); channel .send_eof() - .map_err(|e| TerminalError::WriteFailed(format!("发送 EOF 失败: {}", e)))?; + .map_err(|e| TerminalError::WriteFailed(format!("发送 EOF 失败: {e}")))?; Ok(()) } } diff --git a/src-tauri/src/terminal/connections/wsl_connection.rs b/src-tauri/src/terminal/connections/wsl_connection.rs index 74560b195..03a5ebf79 100644 --- a/src-tauri/src/terminal/connections/wsl_connection.rs +++ b/src-tauri/src/terminal/connections/wsl_connection.rs @@ -112,7 +112,7 @@ impl std::fmt::Display for WSLDistroState { /// - `wsl://distro_name/path` - 指定发行版和初始路径 /// /// _Requirements: 5.1_ -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct WSLOpts { /// 发行版名称(None 表示使用默认发行版) pub distro: Option, @@ -122,16 +122,6 @@ pub struct WSLOpts { pub command: Option, } -impl Default for WSLOpts { - fn default() -> Self { - Self { - distro: None, - initial_path: None, - command: None, - } - } -} - impl WSLOpts { /// 创建新的 WSL 选项 pub fn new() -> Self { @@ -188,8 +178,7 @@ impl WSLOpts { return Ok(Self::default()); } else { return Err(TerminalError::WSLConnectionFailed(format!( - "无效的 WSL 连接字符串,需要以 '{}' 开头: {}", - WSL_CONN_PREFIX, conn_str + "无效的 WSL 连接字符串,需要以 '{WSL_CONN_PREFIX}' 开头: {conn_str}" ))); }; @@ -419,8 +408,7 @@ impl WSLConn { let current_state = self.state(); if !current_state.can_transition_to(ConnectionState::Connecting) { return Err(TerminalError::WSLConnectionFailed(format!( - "无法从 {} 状态开始连接", - current_state + "无法从 {current_state} 状态开始连接" ))); } @@ -447,10 +435,7 @@ impl WSLConn { if !distro_exists { let available: Vec<_> = distros.iter().map(|d| d.name.as_str()).collect(); - let error_msg = format!( - "WSL 发行版 '{}' 不存在。可用的发行版: {:?}", - distro, available - ); + let error_msg = format!("WSL 发行版 '{distro}' 不存在。可用的发行版: {available:?}"); tracing::error!("[WSLConn] {}", error_msg); self.set_state(ConnectionState::Error); self.set_error(Some(error_msg.clone())); @@ -1201,7 +1186,7 @@ mod tests { #[test] fn test_display() { let opts = WSLOpts::new().with_distro("Ubuntu"); - assert_eq!(format!("{}", opts), "wsl://Ubuntu"); + assert_eq!(format!("{opts}"), "wsl://Ubuntu"); } } diff --git a/src-tauri/src/terminal/integration/osc_parser.rs b/src-tauri/src/terminal/integration/osc_parser.rs index 58dd3686c..29015c45f 100644 --- a/src-tauri/src/terminal/integration/osc_parser.rs +++ b/src-tauri/src/terminal/integration/osc_parser.rs @@ -386,7 +386,7 @@ impl OSCParser { /// 完整的 OSC 52 序列 pub fn encode_clipboard(selection: &str, content: &str) -> Vec { let encoded = BASE64.encode(content.as_bytes()); - format!("\x1b]52;{};{}\x07", selection, encoded).into_bytes() + format!("\x1b]52;{selection};{encoded}\x07").into_bytes() } /// 构建 OSC 7 序列 @@ -399,7 +399,7 @@ impl OSCParser { /// 完整的 OSC 7 序列 pub fn build_osc_7(hostname: Option<&str>, path: &str) -> Vec { let host = hostname.unwrap_or(""); - format!("\x1b]7;file://{}{}\x07", host, path).into_bytes() + format!("\x1b]7;file://{host}{path}\x07").into_bytes() } /// 构建 OSC 133 序列 diff --git a/src-tauri/src/terminal/integration/resync.rs b/src-tauri/src/terminal/integration/resync.rs index 5305d263b..299587092 100644 --- a/src-tauri/src/terminal/integration/resync.rs +++ b/src-tauri/src/terminal/integration/resync.rs @@ -91,7 +91,7 @@ impl ResyncOptions { } /// 重同步结果 -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ResyncResult { /// 是否创建了新控制器 pub controller_created: bool, @@ -103,17 +103,6 @@ pub struct ResyncResult { pub history_size: usize, } -impl Default for ResyncResult { - fn default() -> Self { - Self { - controller_created: false, - controller_restarted: false, - history_restored: false, - history_size: 0, - } - } -} - /// 状态重同步控制器 /// /// 提供终端状态重同步功能,用于在连接恢复或控制器状态变更时重建终端状态。 @@ -373,8 +362,7 @@ impl ResyncController { Ok(Box::new(controller)) } _ => Err(TerminalError::Internal(format!( - "未知的控制器类型: {}", - controller_name + "未知的控制器类型: {controller_name}" ))), } } @@ -410,7 +398,7 @@ impl ResyncController { data, }, ) - .map_err(|e| TerminalError::Internal(format!("发送重置序列失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("发送重置序列失败: {e}")))?; tracing::debug!( "[ResyncController] 发送重置序列: block_id={}, full={}", @@ -460,7 +448,7 @@ impl ResyncController { data, }, ) - .map_err(|e| TerminalError::Internal(format!("发送历史数据失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("发送历史数据失败: {e}")))?; tracing::info!( "[ResyncController] 恢复历史数据: block_id={}, size={} bytes", diff --git a/src-tauri/src/terminal/integration/shell_scripts.rs b/src-tauri/src/terminal/integration/shell_scripts.rs index 4f5cfe53c..9d29eba2d 100644 --- a/src-tauri/src/terminal/integration/shell_scripts.rs +++ b/src-tauri/src/terminal/integration/shell_scripts.rs @@ -308,7 +308,7 @@ impl ShellScripts { fn ensure_integration_dir(&self) -> Result<(), TerminalError> { if !self.integration_dir.exists() { fs::create_dir_all(&self.integration_dir) - .map_err(|e| TerminalError::Internal(format!("创建集成脚本目录失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("创建集成脚本目录失败: {e}")))?; } Ok(()) } @@ -348,11 +348,11 @@ impl ShellScripts { fn install_bash_scripts(&self) -> Result<(), TerminalError> { let bash_dir = self.integration_dir.join("bash"); fs::create_dir_all(&bash_dir) - .map_err(|e| TerminalError::Internal(format!("创建 bash 目录失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("创建 bash 目录失败: {e}")))?; let script_path = bash_dir.join("proxycast.bash"); fs::write(&script_path, BASH_INTEGRATION_SCRIPT) - .map_err(|e| TerminalError::Internal(format!("写入 bash 脚本失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("写入 bash 脚本失败: {e}")))?; tracing::debug!("[ShellScripts] Bash 脚本已安装: {}", script_path.display()); @@ -365,17 +365,17 @@ impl ShellScripts { fn install_zsh_scripts(&self) -> Result<(), TerminalError> { let zsh_dir = self.integration_dir.join("zsh"); fs::create_dir_all(&zsh_dir) - .map_err(|e| TerminalError::Internal(format!("创建 zsh 目录失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("创建 zsh 目录失败: {e}")))?; // 写入 .zshenv let zshenv_path = zsh_dir.join(".zshenv"); fs::write(&zshenv_path, ZSH_ZSHENV_SCRIPT) - .map_err(|e| TerminalError::Internal(format!("写入 .zshenv 失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("写入 .zshenv 失败: {e}")))?; // 写入 .zshrc let zshrc_path = zsh_dir.join(".zshrc"); fs::write(&zshrc_path, ZSH_INTEGRATION_SCRIPT) - .map_err(|e| TerminalError::Internal(format!("写入 .zshrc 失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("写入 .zshrc 失败: {e}")))?; tracing::debug!("[ShellScripts] Zsh 脚本已安装: {}", zsh_dir.display()); @@ -388,11 +388,11 @@ impl ShellScripts { fn install_fish_scripts(&self) -> Result<(), TerminalError> { let fish_dir = self.integration_dir.join("fish"); fs::create_dir_all(&fish_dir) - .map_err(|e| TerminalError::Internal(format!("创建 fish 目录失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("创建 fish 目录失败: {e}")))?; let script_path = fish_dir.join("proxycast.fish"); fs::write(&script_path, FISH_INTEGRATION_SCRIPT) - .map_err(|e| TerminalError::Internal(format!("写入 fish 脚本失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("写入 fish 脚本失败: {e}")))?; tracing::debug!("[ShellScripts] Fish 脚本已安装: {}", script_path.display()); @@ -403,11 +403,11 @@ impl ShellScripts { fn install_pwsh_scripts(&self) -> Result<(), TerminalError> { let pwsh_dir = self.integration_dir.join("pwsh"); fs::create_dir_all(&pwsh_dir) - .map_err(|e| TerminalError::Internal(format!("创建 pwsh 目录失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("创建 pwsh 目录失败: {e}")))?; let script_path = pwsh_dir.join("proxycast.ps1"); fs::write(&script_path, PWSH_INTEGRATION_SCRIPT) - .map_err(|e| TerminalError::Internal(format!("写入 pwsh 脚本失败: {}", e)))?; + .map_err(|e| TerminalError::Internal(format!("写入 pwsh 脚本失败: {e}")))?; tracing::debug!( "[ShellScripts] PowerShell 脚本已安装: {}", @@ -690,7 +690,7 @@ impl ShellLaunchBuilder { Ok(config .arg("-NoExit") .arg("-Command") - .arg(format!(". '{}'", script_path_str))) + .arg(format!(". '{script_path_str}'"))) } } diff --git a/src-tauri/src/terminal/persistence/block_file.rs b/src-tauri/src/terminal/persistence/block_file.rs index 85ec8bf95..2c2347b6b 100644 --- a/src-tauri/src/terminal/persistence/block_file.rs +++ b/src-tauri/src/terminal/persistence/block_file.rs @@ -59,19 +59,19 @@ impl BlockFile { /// /// _Requirements: 3.1, 3.3_ pub fn new(block_id: &str, base_dir: &PathBuf, max_size: usize) -> Result { - let file_path = base_dir.join(format!("{}.block", block_id)); + let file_path = base_dir.join(format!("{block_id}.block")); // 确保目录存在 if let Some(parent) = file_path.parent() { fs::create_dir_all(parent).map_err(|e| { - TerminalError::BlockFileError(format!("无法创建目录 {:?}: {}", parent, e)) + TerminalError::BlockFileError(format!("无法创建目录 {parent:?}: {e}")) })?; } // 检查文件是否已存在,获取当前大小 let (current_size, is_wrapped) = if file_path.exists() { let metadata = fs::metadata(&file_path) - .map_err(|e| TerminalError::BlockFileError(format!("无法读取文件元数据: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("无法读取文件元数据: {e}")))?; let size = metadata.len() as usize; // 如果文件大小已经达到最大值,说明已经循环过 (size, size >= max_size) @@ -85,7 +85,7 @@ impl BlockFile { .read(true) .write(true) .open(&file_path) - .map_err(|e| TerminalError::BlockFileError(format!("无法打开文件: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("无法打开文件: {e}")))?; tracing::debug!( "[BlockFile] 创建块文件: {} (max_size: {}, current_size: {})", @@ -178,11 +178,11 @@ impl BlockFile { if new_total <= self.max_size { // 文件未满,直接追加 file.seek(SeekFrom::End(0)) - .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {e}")))?; file.write_all(data_to_write) - .map_err(|e| TerminalError::BlockFileError(format!("写入失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("写入失败: {e}")))?; file.flush() - .map_err(|e| TerminalError::BlockFileError(format!("Flush 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Flush 失败: {e}")))?; self.current_size.store(new_total, Ordering::Relaxed); self.write_pos.store(new_total, Ordering::Relaxed); } else { @@ -201,12 +201,12 @@ impl BlockFile { fn apply_circular_buffer(&self, file: &mut File, new_data: &[u8]) -> Result<(), TerminalError> { // 读取现有数据 file.seek(SeekFrom::Start(0)) - .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {e}")))?; let current_size = self.current_size.load(Ordering::Relaxed); let mut existing_data = vec![0u8; current_size]; file.read_exact(&mut existing_data) - .map_err(|e| TerminalError::BlockFileError(format!("读取失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("读取失败: {e}")))?; // 合并数据 let mut combined = existing_data; @@ -221,13 +221,13 @@ impl BlockFile { // 重写文件 file.seek(SeekFrom::Start(0)) - .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {e}")))?; file.write_all(final_data) - .map_err(|e| TerminalError::BlockFileError(format!("写入失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("写入失败: {e}")))?; file.set_len(final_data.len() as u64) - .map_err(|e| TerminalError::BlockFileError(format!("截断失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("截断失败: {e}")))?; file.flush() - .map_err(|e| TerminalError::BlockFileError(format!("Flush 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Flush 失败: {e}")))?; self.current_size.store(final_data.len(), Ordering::Relaxed); self.write_pos.store(final_data.len(), Ordering::Relaxed); @@ -255,11 +255,11 @@ impl BlockFile { } file.seek(SeekFrom::Start(0)) - .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {e}")))?; let mut data = vec![0u8; current_size]; file.read_exact(&mut data) - .map_err(|e| TerminalError::BlockFileError(format!("读取失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("读取失败: {e}")))?; Ok(data) } @@ -278,11 +278,11 @@ impl BlockFile { .ok_or_else(|| TerminalError::BlockFileError("文件已关闭".to_string()))?; file.set_len(0) - .map_err(|e| TerminalError::BlockFileError(format!("截断失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("截断失败: {e}")))?; file.seek(SeekFrom::Start(0)) - .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Seek 失败: {e}")))?; file.flush() - .map_err(|e| TerminalError::BlockFileError(format!("Flush 失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("Flush 失败: {e}")))?; self.current_size.store(0, Ordering::Relaxed); self.write_pos.store(0, Ordering::Relaxed); @@ -305,7 +305,7 @@ impl BlockFile { // 删除文件 if self.file_path.exists() { fs::remove_file(&self.file_path) - .map_err(|e| TerminalError::BlockFileError(format!("删除文件失败: {}", e)))?; + .map_err(|e| TerminalError::BlockFileError(format!("删除文件失败: {e}")))?; } tracing::debug!("[BlockFile] 删除块文件: {}", self.block_id); diff --git a/src-tauri/src/terminal/persistence/session_store.rs b/src-tauri/src/terminal/persistence/session_store.rs index 95999f37c..b59ebbde7 100644 --- a/src-tauri/src/terminal/persistence/session_store.rs +++ b/src-tauri/src/terminal/persistence/session_store.rs @@ -87,7 +87,7 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; conn.execute( "CREATE TABLE IF NOT EXISTS terminal_sessions ( @@ -103,26 +103,26 @@ impl SessionMetadataStore { )", [], ) - .map_err(|e| TerminalError::DatabaseError(format!("创建表失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("创建表失败: {e}")))?; // 创建索引 conn.execute( "CREATE INDEX IF NOT EXISTS idx_terminal_sessions_block_id ON terminal_sessions(block_id)", [], ) - .map_err(|e| TerminalError::DatabaseError(format!("创建索引失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("创建索引失败: {e}")))?; conn.execute( "CREATE INDEX IF NOT EXISTS idx_terminal_sessions_tab_id ON terminal_sessions(tab_id)", [], ) - .map_err(|e| TerminalError::DatabaseError(format!("创建索引失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("创建索引失败: {e}")))?; conn.execute( "CREATE INDEX IF NOT EXISTS idx_terminal_sessions_status ON terminal_sessions(status)", [], ) - .map_err(|e| TerminalError::DatabaseError(format!("创建索引失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("创建索引失败: {e}")))?; tracing::debug!("[SessionStore] 数据库表初始化完成"); Ok(()) @@ -137,7 +137,7 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; conn.execute( "INSERT OR REPLACE INTO terminal_sessions @@ -155,7 +155,7 @@ impl SessionMetadataStore { record.exit_code, ], ) - .map_err(|e| TerminalError::DatabaseError(format!("保存会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("保存会话失败: {e}")))?; tracing::debug!("[SessionStore] 保存会话: {}", record.id); Ok(()) @@ -166,7 +166,7 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let result = conn .query_row( @@ -188,7 +188,7 @@ impl SessionMetadataStore { }, ) .optional() - .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {e}")))?; Ok(result) } @@ -198,7 +198,7 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let result = conn .query_row( @@ -220,7 +220,7 @@ impl SessionMetadataStore { }, ) .optional() - .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {e}")))?; Ok(result) } @@ -230,14 +230,14 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let mut stmt = conn .prepare( "SELECT id, block_id, tab_id, controller_type, connection, status, created_at, updated_at, exit_code FROM terminal_sessions ORDER BY created_at DESC", ) - .map_err(|e| TerminalError::DatabaseError(format!("准备查询失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("准备查询失败: {e}")))?; let records = stmt .query_map([], |row| { @@ -253,9 +253,9 @@ impl SessionMetadataStore { exit_code: row.get(8)?, }) }) - .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {}", e)))? + .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {e}")))? .collect::, _>>() - .map_err(|e| TerminalError::DatabaseError(format!("读取会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("读取会话失败: {e}")))?; Ok(records) } @@ -265,14 +265,14 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let mut stmt = conn .prepare( "SELECT id, block_id, tab_id, controller_type, connection, status, created_at, updated_at, exit_code FROM terminal_sessions WHERE status = ?1 ORDER BY created_at DESC", ) - .map_err(|e| TerminalError::DatabaseError(format!("准备查询失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("准备查询失败: {e}")))?; let records = stmt .query_map(params![status], |row| { @@ -288,9 +288,9 @@ impl SessionMetadataStore { exit_code: row.get(8)?, }) }) - .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {}", e)))? + .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {e}")))? .collect::, _>>() - .map_err(|e| TerminalError::DatabaseError(format!("读取会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("读取会话失败: {e}")))?; Ok(records) } @@ -300,14 +300,14 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let mut stmt = conn .prepare( "SELECT id, block_id, tab_id, controller_type, connection, status, created_at, updated_at, exit_code FROM terminal_sessions WHERE tab_id = ?1 ORDER BY created_at DESC", ) - .map_err(|e| TerminalError::DatabaseError(format!("准备查询失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("准备查询失败: {e}")))?; let records = stmt .query_map(params![tab_id], |row| { @@ -323,9 +323,9 @@ impl SessionMetadataStore { exit_code: row.get(8)?, }) }) - .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {}", e)))? + .map_err(|e| TerminalError::DatabaseError(format!("查询会话失败: {e}")))? .collect::, _>>() - .map_err(|e| TerminalError::DatabaseError(format!("读取会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("读取会话失败: {e}")))?; Ok(records) } @@ -342,7 +342,7 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let now = Utc::now().timestamp_millis(); @@ -350,7 +350,7 @@ impl SessionMetadataStore { "UPDATE terminal_sessions SET status = ?1, exit_code = ?2, updated_at = ?3 WHERE id = ?4", params![status, exit_code, now, id], ) - .map_err(|e| TerminalError::DatabaseError(format!("更新会话状态失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("更新会话状态失败: {e}")))?; tracing::debug!("[SessionStore] 更新会话状态: {} -> {}", id, status); Ok(()) @@ -361,10 +361,10 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; conn.execute("DELETE FROM terminal_sessions WHERE id = ?1", params![id]) - .map_err(|e| TerminalError::DatabaseError(format!("删除会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("删除会话失败: {e}")))?; tracing::debug!("[SessionStore] 删除会话: {}", id); Ok(()) @@ -375,14 +375,14 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let count = conn .execute( "DELETE FROM terminal_sessions WHERE tab_id = ?1", params![tab_id], ) - .map_err(|e| TerminalError::DatabaseError(format!("删除会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("删除会话失败: {e}")))?; tracing::debug!("[SessionStore] 删除标签页 {} 的 {} 个会话", tab_id, count); Ok(count) @@ -395,14 +395,14 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let count = conn .execute( "DELETE FROM terminal_sessions WHERE status = 'done' AND created_at < ?1", params![before_timestamp], ) - .map_err(|e| TerminalError::DatabaseError(format!("清理会话失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("清理会话失败: {e}")))?; if count > 0 { tracing::info!("[SessionStore] 清理了 {} 个旧会话", count); @@ -415,13 +415,13 @@ impl SessionMetadataStore { let conn = self .db .lock() - .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("无法获取数据库锁: {e}")))?; let count: i64 = conn .query_row("SELECT COUNT(*) FROM terminal_sessions", [], |row| { row.get(0) }) - .map_err(|e| TerminalError::DatabaseError(format!("查询会话数量失败: {}", e)))?; + .map_err(|e| TerminalError::DatabaseError(format!("查询会话数量失败: {e}")))?; Ok(count as usize) } diff --git a/src-tauri/src/terminal/session_manager.rs b/src-tauri/src/terminal/session_manager.rs index 08af74700..7b6fec9f8 100644 --- a/src-tauri/src/terminal/session_manager.rs +++ b/src-tauri/src/terminal/session_manager.rs @@ -309,7 +309,7 @@ impl TerminalSessionManager { ) -> Result<(), TerminalError> { let decoded = BASE64 .decode(data) - .map_err(|e| TerminalError::WriteFailed(format!("Base64 解码失败: {}", e)))?; + .map_err(|e| TerminalError::WriteFailed(format!("Base64 解码失败: {e}")))?; self.write_to_session(session_id, &decoded).await } @@ -434,13 +434,10 @@ impl TerminalSessionManager { .ok_or_else(|| TerminalError::SessionNotFound(session_id.to_string()))?; // 检查块文件是否存在 - let block_file_path = self - .block_file_base_dir - .join(format!("{}.block", session_id)); + let block_file_path = self.block_file_base_dir.join(format!("{session_id}.block")); if !block_file_path.exists() { return Err(TerminalError::BlockFileError(format!( - "块文件不存在: {:?}", - block_file_path + "块文件不存在: {block_file_path:?}" ))); } diff --git a/src-tauri/src/translator/kiro/anthropic/request.rs b/src-tauri/src/translator/kiro/anthropic/request.rs index d3bdd1635..d98b78b95 100644 --- a/src-tauri/src/translator/kiro/anthropic/request.rs +++ b/src-tauri/src/translator/kiro/anthropic/request.rs @@ -554,7 +554,7 @@ fn convert_anthropic_tools(tools: &Option>) -> Option 500 { let truncated: String = desc.chars().take(497).collect(); - format!("{}...", truncated) + format!("{truncated}...") } else { desc }, diff --git a/src-tauri/src/translator/kiro/openai/request.rs b/src-tauri/src/translator/kiro/openai/request.rs index ad493503f..66d78c6ff 100644 --- a/src-tauri/src/translator/kiro/openai/request.rs +++ b/src-tauri/src/translator/kiro/openai/request.rs @@ -476,7 +476,7 @@ fn convert_tools(tools: &Option>) -> Option> { name: function.name.clone(), description: if desc.len() > 500 { let truncated: String = desc.chars().take(497).collect(); - format!("{}...", truncated) + format!("{truncated}...") } else { desc }, diff --git a/src-tauri/src/translator/traits.rs b/src-tauri/src/translator/traits.rs index 9ff4fe5c5..cae70847a 100644 --- a/src-tauri/src/translator/traits.rs +++ b/src-tauri/src/translator/traits.rs @@ -180,7 +180,7 @@ impl TranslateError { pub fn missing_field(field: &str) -> Self { Self::new( TranslateErrorKind::MissingField, - format!("Missing required field: {}", field), + format!("Missing required field: {field}"), ) } } @@ -192,7 +192,7 @@ mod tests { #[test] fn test_translate_error_display() { let err = TranslateError::new(TranslateErrorKind::InvalidRequest, "test error"); - assert_eq!(format!("{}", err), "InvalidRequest: test error"); + assert_eq!(format!("{err}"), "InvalidRequest: test error"); } #[test] diff --git a/src-tauri/src/tray/sync.rs b/src-tauri/src/tray/sync.rs index 86a7a8dc4..feaab9959 100644 --- a/src-tauri/src/tray/sync.rs +++ b/src-tauri/src/tray/sync.rs @@ -64,7 +64,7 @@ impl TraySynchronizer { icon_status, server_running, server_address: if server_running { - format!("{}:{}", server_host, server_port) + format!("{server_host}:{server_port}") } else { String::new() }, @@ -109,7 +109,7 @@ impl TraySynchronizer { // 更新服务器相关字段 current_state.server_running = server_running; current_state.server_address = if server_running { - format!("{}:{}", server_host, server_port) + format!("{server_host}:{server_port}") } else { String::new() }; diff --git a/src-tauri/src/voice/asr_service.rs b/src-tauri/src/voice/asr_service.rs index 2d534fc19..4c3a499a8 100644 --- a/src-tauri/src/voice/asr_service.rs +++ b/src-tauri/src/voice/asr_service.rs @@ -102,8 +102,7 @@ impl AsrService { tracing::error!("本地 Whisper 回退也失败: {}", whisper_error); // 返回原始云端错误,因为那是用户选择的服务 Err(format!( - "云端服务失败: {};本地 Whisper 回退也失败: {}", - cloud_error, whisper_error + "云端服务失败: {cloud_error};本地 Whisper 回退也失败: {whisper_error}" )) } } @@ -111,15 +110,13 @@ impl AsrService { Ok(None) => { tracing::warn!("未找到本地 Whisper 凭证,无法回退"); Err(format!( - "云端服务失败: {};未配置本地 Whisper,无法回退", - cloud_error + "云端服务失败: {cloud_error};未配置本地 Whisper,无法回退" )) } Err(e) => { tracing::error!("获取本地 Whisper 凭证失败: {}", e); Err(format!( - "云端服务失败: {};获取本地 Whisper 凭证失败: {}", - cloud_error, e + "云端服务失败: {cloud_error};获取本地 Whisper 凭证失败: {e}" )) } } @@ -175,12 +172,12 @@ impl AsrService { // 创建 Whisper 识别器 let transcriber = voice_core::WhisperTranscriber::new(model_path, model, &credential.language) - .map_err(|e| format!("Whisper 模型加载失败: {}", e))?; + .map_err(|e| format!("Whisper 模型加载失败: {e}"))?; // 执行识别 let result = transcriber .transcribe(&audio) - .map_err(|e| format!("Whisper 识别失败: {}", e))?; + .map_err(|e| format!("Whisper 识别失败: {e}"))?; Ok(result.text) } @@ -244,7 +241,7 @@ impl AsrService { let mut body = Vec::new(); // 添加 file 字段 - body.extend_from_slice(format!("--{}\r\n", boundary).as_bytes()); + body.extend_from_slice(format!("--{boundary}\r\n").as_bytes()); body.extend_from_slice( b"Content-Disposition: form-data; name=\"file\"; filename=\"audio.wav\"\r\n", ); @@ -253,25 +250,25 @@ impl AsrService { body.extend_from_slice(b"\r\n"); // 添加 model 字段 - body.extend_from_slice(format!("--{}\r\n", boundary).as_bytes()); + body.extend_from_slice(format!("--{boundary}\r\n").as_bytes()); body.extend_from_slice(b"Content-Disposition: form-data; name=\"model\"\r\n\r\n"); body.extend_from_slice(b"whisper-1\r\n"); // 添加 language 字段 - body.extend_from_slice(format!("--{}\r\n", boundary).as_bytes()); + body.extend_from_slice(format!("--{boundary}\r\n").as_bytes()); body.extend_from_slice(b"Content-Disposition: form-data; name=\"language\"\r\n\r\n"); body.extend_from_slice(credential.language.as_bytes()); body.extend_from_slice(b"\r\n"); // 结束边界 - body.extend_from_slice(format!("--{}--\r\n", boundary).as_bytes()); + body.extend_from_slice(format!("--{boundary}--\r\n").as_bytes()); // 构建请求 let base_url = config .base_url .as_deref() .unwrap_or("https://api.openai.com"); - let url = format!("{}/v1/audio/transcriptions", base_url); + let url = format!("{base_url}/v1/audio/transcriptions"); let client = reqwest::Client::new(); let response = client @@ -279,17 +276,17 @@ impl AsrService { .header("Authorization", format!("Bearer {}", config.api_key)) .header( "Content-Type", - format!("multipart/form-data; boundary={}", boundary), + format!("multipart/form-data; boundary={boundary}"), ) .body(body) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("OpenAI API 错误: {} - {}", status, body)); + return Err(format!("OpenAI API 错误: {status} - {body}")); } #[derive(serde::Deserialize)] @@ -300,7 +297,7 @@ impl AsrService { let result: WhisperResponse = response .json() .await - .map_err(|e| format!("解析响应失败: {}", e))?; + .map_err(|e| format!("解析响应失败: {e}"))?; Ok(result.text) } @@ -324,7 +321,7 @@ impl AsrService { .post(&token_url) .send() .await - .map_err(|e| format!("获取 Token 失败: {}", e))?; + .map_err(|e| format!("获取 Token 失败: {e}"))?; #[derive(serde::Deserialize)] struct TokenResponse { @@ -334,7 +331,7 @@ impl AsrService { let token: TokenResponse = token_resp .json() .await - .map_err(|e| format!("解析 Token 失败: {}", e))?; + .map_err(|e| format!("解析 Token 失败: {e}"))?; // 构建 WAV 并 Base64 编码 let wav_data = Self::build_wav(audio_data, sample_rate, 1)?; @@ -366,7 +363,7 @@ impl AsrService { .json(&request) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; #[derive(serde::Deserialize)] struct AsrResponse { @@ -379,7 +376,7 @@ impl AsrService { let result: AsrResponse = response .json() .await - .map_err(|e| format!("解析响应失败: {}", e))?; + .map_err(|e| format!("解析响应失败: {e}"))?; if result.err_no != 0 { return Err(format!( @@ -430,7 +427,7 @@ impl AsrService { let result = client .transcribe(&audio) .await - .map_err(|e| format!("讯飞识别失败: {}", e))?; + .map_err(|e| format!("讯飞识别失败: {e}"))?; Ok(result.text) } diff --git a/src-tauri/src/voice/commands.rs b/src-tauri/src/voice/commands.rs index 9b0e76eb9..9426466a5 100644 --- a/src-tauri/src/voice/commands.rs +++ b/src-tauri/src/voice/commands.rs @@ -145,7 +145,7 @@ pub async fn transcribe_audio( // 获取凭证 let credential = if let Some(id) = credential_id { tracing::info!("[语音识别] 使用指定凭证: {}", id); - AsrService::get_credential(&id)?.ok_or_else(|| format!("凭证不存在: {}", id))? + AsrService::get_credential(&id)?.ok_or_else(|| format!("凭证不存在: {id}"))? } else { tracing::info!("[语音识别] 获取默认凭证..."); match AsrService::get_default_credential() { @@ -178,7 +178,7 @@ pub async fn transcribe_audio( } Err(e) => { tracing::error!("[语音识别] 获取默认凭证失败: {}", e); - return Err(format!("获取凭证失败: {}", e)); + return Err(format!("获取凭证失败: {e}")); } } }; @@ -226,7 +226,7 @@ pub async fn polish_voice_text( .instructions .iter() .find(|i| i.id == instruction_id) - .ok_or_else(|| format!("指令不存在: {}", instruction_id))?; + .ok_or_else(|| format!("指令不存在: {instruction_id}"))?; // 如果是原始输出,直接返回 if instruction_id == "raw" { @@ -268,7 +268,7 @@ pub async fn output_voice_text(text: String, mode: Option) -> Result<(), let config = config::load_voice_config()?; config.output.mode } - Some(other) => return Err(format!("未知的输出模式: {}", other)), + Some(other) => return Err(format!("未知的输出模式: {other}")), }; // 执行输出 diff --git a/src-tauri/src/voice/output_service.rs b/src-tauri/src/voice/output_service.rs index 02f282ab4..ec175dc86 100644 --- a/src-tauri/src/voice/output_service.rs +++ b/src-tauri/src/voice/output_service.rs @@ -24,11 +24,9 @@ fn type_text(text: &str) -> Result<(), String> { use enigo::{Enigo, Keyboard, Settings}; let mut enigo = - Enigo::new(&Settings::default()).map_err(|e| format!("初始化键盘模拟器失败: {}", e))?; + Enigo::new(&Settings::default()).map_err(|e| format!("初始化键盘模拟器失败: {e}"))?; - enigo - .text(text) - .map_err(|e| format!("键盘输入失败: {}", e))?; + enigo.text(text).map_err(|e| format!("键盘输入失败: {e}"))?; tracing::info!("[语音输出] 键盘输入完成: {} 字符", text.chars().count()); Ok(()) @@ -36,11 +34,11 @@ fn type_text(text: &str) -> Result<(), String> { /// 复制到剪贴板 fn copy_to_clipboard(text: &str) -> Result<(), String> { - let mut clipboard = Clipboard::new().map_err(|e| format!("初始化剪贴板失败: {}", e))?; + let mut clipboard = Clipboard::new().map_err(|e| format!("初始化剪贴板失败: {e}"))?; clipboard .set_text(text) - .map_err(|e| format!("复制到剪贴板失败: {}", e))?; + .map_err(|e| format!("复制到剪贴板失败: {e}"))?; tracing::info!("[语音输出] 已复制到剪贴板: {} 字符", text.chars().count()); Ok(()) diff --git a/src-tauri/src/voice/processor.rs b/src-tauri/src/voice/processor.rs index 017bdb43b..505e4bbd9 100644 --- a/src-tauri/src/voice/processor.rs +++ b/src-tauri/src/voice/processor.rs @@ -125,18 +125,18 @@ async fn call_local_llm( // 发送请求 let client = reqwest::Client::new(); let response = client - .post(format!("{}/v1/chat/completions", base_url)) - .header("Authorization", format!("Bearer {}", api_key)) + .post(format!("{base_url}/v1/chat/completions")) + .header("Authorization", format!("Bearer {api_key}")) .header("Content-Type", "application/json") .json(&request) .send() .await - .map_err(|e| format!("请求失败: {}", e))?; + .map_err(|e| format!("请求失败: {e}"))?; if !response.status().is_success() { let status = response.status(); let body = response.text().await.unwrap_or_default(); - return Err(format!("LLM API 错误: {} - {}", status, body)); + return Err(format!("LLM API 错误: {status} - {body}")); } // 解析响应 @@ -158,7 +158,7 @@ async fn call_local_llm( let result: ChatResponse = response .json() .await - .map_err(|e| format!("解析响应失败: {}", e))?; + .map_err(|e| format!("解析响应失败: {e}"))?; result .choices diff --git a/src-tauri/src/voice/recording_service.rs b/src-tauri/src/voice/recording_service.rs index 86264c7b3..6521a5ce4 100644 --- a/src-tauri/src/voice/recording_service.rs +++ b/src-tauri/src/voice/recording_service.rs @@ -49,7 +49,7 @@ pub fn list_audio_devices() -> Result, String> { let devices: Vec = host .input_devices() - .map_err(|e| format!("无法枚举音频设备: {}", e))? + .map_err(|e| format!("无法枚举音频设备: {e}"))? .filter_map(|device| { let name = device.name().ok()?; let is_default = default_name.as_ref().map(|n| n == &name).unwrap_or(false); @@ -151,7 +151,7 @@ impl RecordingService { let rx = self.response_rx.as_ref().ok_or("录音线程未启动")?; tx.send(RecordingCommand::Start(device_id)) - .map_err(|e| format!("发送命令失败: {}", e))?; + .map_err(|e| format!("发送命令失败: {e}"))?; match rx.recv() { Ok(RecordingResponse::Ok) => { @@ -160,7 +160,7 @@ impl RecordingService { } Ok(RecordingResponse::Error(e)) => Err(e), Ok(_) => Err("意外的响应".to_string()), - Err(e) => Err(format!("接收响应失败: {}", e)), + Err(e) => Err(format!("接收响应失败: {e}")), } } @@ -170,7 +170,7 @@ impl RecordingService { let rx = self.response_rx.as_ref().ok_or("录音线程未启动")?; tx.send(RecordingCommand::Stop) - .map_err(|e| format!("发送命令失败: {}", e))?; + .map_err(|e| format!("发送命令失败: {e}"))?; match rx.recv() { Ok(RecordingResponse::AudioData(audio)) => { @@ -179,7 +179,7 @@ impl RecordingService { } Ok(RecordingResponse::Error(e)) => Err(e), Ok(_) => Err("意外的响应".to_string()), - Err(e) => Err(format!("接收响应失败: {}", e)), + Err(e) => Err(format!("接收响应失败: {e}")), } } @@ -319,7 +319,7 @@ fn recording_thread_main( Ok(c) => c, Err(e) => { let _ = resp_tx - .send(RecordingResponse::Error(format!("获取音频配置失败: {}", e))); + .send(RecordingResponse::Error(format!("获取音频配置失败: {e}"))); continue; } }; @@ -405,15 +405,15 @@ fn recording_thread_main( ) { Ok(s) => s, Err(e) => { - let _ = resp_tx - .send(RecordingResponse::Error(format!("创建音频流失败: {}", e))); + let _ = + resp_tx.send(RecordingResponse::Error(format!("创建音频流失败: {e}"))); continue; } }; // 开始播放(录音) if let Err(e) = stream.play() { - let _ = resp_tx.send(RecordingResponse::Error(format!("启动录音失败: {}", e))); + let _ = resp_tx.send(RecordingResponse::Error(format!("启动录音失败: {e}"))); continue; } diff --git a/src-tauri/src/voice/shortcut.rs b/src-tauri/src/voice/shortcut.rs index 99cccbe54..01f5c135a 100644 --- a/src-tauri/src/voice/shortcut.rs +++ b/src-tauri/src/voice/shortcut.rs @@ -35,13 +35,13 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), String> { // 解析快捷键 let shortcut: Shortcut = shortcut_str .parse() - .map_err(|e| format!("无效的快捷键: {}", e))?; + .map_err(|e| format!("无效的快捷键: {e}"))?; // 获取全局快捷键管理器 let global_shortcut = app.global_shortcut(); // 检查快捷键是否已被注册 - let is_already_registered = global_shortcut.is_registered(shortcut.clone()); + let is_already_registered = global_shortcut.is_registered(shortcut); debug!( "[语音输入] 快捷键 {} 是否已注册: {}", shortcut_str, is_already_registered @@ -52,11 +52,11 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), String> { // 如果是我们自己注册的,先注销 if IS_REGISTERED.load(Ordering::SeqCst) { info!("[语音输入] 尝试注销已有的快捷键"); - if let Err(e) = global_shortcut.unregister(shortcut.clone()) { + if let Err(e) = global_shortcut.unregister(shortcut) { error!("[语音输入] 注销已有快捷键失败: {}", e); } } else { - return Err(format!("快捷键已被占用: {}", shortcut_str)); + return Err(format!("快捷键已被占用: {shortcut_str}")); } } @@ -66,7 +66,7 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), String> { // 注册快捷键 info!("[语音输入] 开始注册快捷键回调..."); global_shortcut - .on_shortcut(shortcut.clone(), move |_app, _shortcut, event| { + .on_shortcut(shortcut, move |_app, _shortcut, event| { if event.state == ShortcutState::Pressed { info!("[语音输入] 快捷键按下"); // 打开截图输入框(语音模式) @@ -85,7 +85,7 @@ pub fn register(app: &AppHandle, shortcut_str: &str) -> Result<(), String> { }) .map_err(|e| { error!("[语音输入] 注册快捷键失败: {}", e); - format!("注册快捷键失败: {}", e) + format!("注册快捷键失败: {e}") })?; // 更新状态 @@ -105,14 +105,14 @@ pub fn unregister(app: &AppHandle) -> Result<(), String> { let shortcut: Shortcut = shortcut_str .parse() - .map_err(|e| format!("解析快捷键失败: {}", e))?; + .map_err(|e| format!("解析快捷键失败: {e}"))?; let global_shortcut = app.global_shortcut(); - if global_shortcut.is_registered(shortcut.clone()) { + if global_shortcut.is_registered(shortcut) { global_shortcut .unregister(shortcut) - .map_err(|e| format!("注销快捷键失败: {}", e))?; + .map_err(|e| format!("注销快捷键失败: {e}"))?; } // 更新状态 @@ -180,13 +180,13 @@ pub fn register_translate( // 解析快捷键 let shortcut: Shortcut = shortcut_str .parse() - .map_err(|e| format!("无效的快捷键: {}", e))?; + .map_err(|e| format!("无效的快捷键: {e}"))?; // 获取全局快捷键管理器 let global_shortcut = app.global_shortcut(); // 检查快捷键是否已被注册 - let is_already_registered = global_shortcut.is_registered(shortcut.clone()); + let is_already_registered = global_shortcut.is_registered(shortcut); debug!( "[语音输入] 翻译快捷键 {} 是否已注册: {}", shortcut_str, is_already_registered @@ -197,11 +197,11 @@ pub fn register_translate( // 如果是我们自己注册的,先注销 if IS_TRANSLATE_REGISTERED.load(Ordering::SeqCst) { info!("[语音输入] 尝试注销已有的翻译快捷键"); - if let Err(e) = global_shortcut.unregister(shortcut.clone()) { + if let Err(e) = global_shortcut.unregister(shortcut) { error!("[语音输入] 注销已有翻译快捷键失败: {}", e); } } else { - return Err(format!("快捷键已被占用: {}", shortcut_str)); + return Err(format!("快捷键已被占用: {shortcut_str}")); } } @@ -212,7 +212,7 @@ pub fn register_translate( // 注册快捷键 info!("[语音输入] 开始注册翻译快捷键回调..."); global_shortcut - .on_shortcut(shortcut.clone(), move |_app, _shortcut, event| { + .on_shortcut(shortcut, move |_app, _shortcut, event| { if event.state == ShortcutState::Pressed { info!("[语音输入] 翻译快捷键按下"); // 打开截图输入框(翻译模式) @@ -232,7 +232,7 @@ pub fn register_translate( }) .map_err(|e| { error!("[语音输入] 注册翻译快捷键失败: {}", e); - format!("注册翻译快捷键失败: {}", e) + format!("注册翻译快捷键失败: {e}") })?; // 更新状态 @@ -252,14 +252,14 @@ pub fn unregister_translate(app: &AppHandle) -> Result<(), String> { let shortcut: Shortcut = shortcut_str .parse() - .map_err(|e| format!("解析快捷键失败: {}", e))?; + .map_err(|e| format!("解析快捷键失败: {e}"))?; let global_shortcut = app.global_shortcut(); - if global_shortcut.is_registered(shortcut.clone()) { + if global_shortcut.is_registered(shortcut) { global_shortcut .unregister(shortcut) - .map_err(|e| format!("注销翻译快捷键失败: {}", e))?; + .map_err(|e| format!("注销翻译快捷键失败: {e}"))?; } // 更新状态 diff --git a/src-tauri/src/voice/window.rs b/src-tauri/src/voice/window.rs index a6567d421..8aeb7341b 100644 --- a/src-tauri/src/voice/window.rs +++ b/src-tauri/src/voice/window.rs @@ -15,7 +15,7 @@ pub fn open_voice_window(app: &AppHandle) -> Result<(), String> { // 发送重置事件,让前端重新开始录音 window .emit("voice-reset", ()) - .map_err(|e| format!("发送重置事件失败: {}", e))?; + .map_err(|e| format!("发送重置事件失败: {e}"))?; // 移动到鼠标所在屏幕 position_window_on_cursor_screen(&window)?; @@ -40,7 +40,7 @@ pub fn open_voice_window(app: &AppHandle) -> Result<(), String> { .transparent(false) // 关闭透明,避免 macOS 上的渲染问题 .skip_taskbar(true) .build() - .map_err(|e| format!("创建窗口失败: {}", e))?; + .map_err(|e| format!("创建窗口失败: {e}"))?; // 移动到鼠标所在屏幕 position_window_on_cursor_screen(&window)?; @@ -137,7 +137,7 @@ pub fn send_stop_recording_event(app: &AppHandle) -> Result<(), String> { if let Some(window) = app.get_webview_window(VOICE_WINDOW_LABEL) { window .emit("voice-stop-recording", ()) - .map_err(|e| format!("发送停止录音事件失败: {}", e))?; + .map_err(|e| format!("发送停止录音事件失败: {e}"))?; tracing::info!("[语音输入] 已发送停止录音事件"); } Ok(()) diff --git a/src-tauri/src/websocket/handler.rs b/src-tauri/src/websocket/handler.rs index b5ce714ec..18d4a7206 100644 --- a/src-tauri/src/websocket/handler.rs +++ b/src-tauri/src/websocket/handler.rs @@ -136,7 +136,7 @@ async fn handle_socket(socket: WebSocket, state: WsHandlerState, client_info: Op MAX_MESSAGE_SIZE ))); let error_text = serde_json::to_string(&error).unwrap_or_default(); - if sender.send(Message::Text(error_text.into())).await.is_err() { + if sender.send(Message::Text(error_text)).await.is_err() { break; } continue; @@ -158,8 +158,7 @@ async fn handle_socket(socket: WebSocket, state: WsHandlerState, client_info: Op Err(e) => { state.manager.on_error(); let error = WsMessage::Error(WsError::invalid_message(format!( - "Failed to parse message: {}", - e + "Failed to parse message: {e}" ))); let error_text = serde_json::to_string(&error).unwrap_or_default(); if sender.send(Message::Text(error_text)).await.is_err() { @@ -174,7 +173,7 @@ async fn handle_socket(socket: WebSocket, state: WsHandlerState, client_info: Op let error = WsMessage::Error(WsError::invalid_message("Binary messages not supported")); let error_text = serde_json::to_string(&error).unwrap_or_default(); - if sender.send(Message::Text(error_text.into())).await.is_err() { + if sender.send(Message::Text(error_text)).await.is_err() { break; } } @@ -317,11 +316,10 @@ async fn handle_api_request(_state: &WsHandlerState, request: &WsApiRequest) -> /// 解析 WebSocket 消息 pub fn parse_message(text: &str) -> Result { - serde_json::from_str(text).map_err(|e| WsError::invalid_message(format!("Parse error: {}", e))) + serde_json::from_str(text).map_err(|e| WsError::invalid_message(format!("Parse error: {e}"))) } /// 序列化 WebSocket 消息 pub fn serialize_message(msg: &WsMessage) -> Result { - serde_json::to_string(msg) - .map_err(|e| WsError::internal(None, format!("Serialize error: {}", e))) + serde_json::to_string(msg).map_err(|e| WsError::internal(None, format!("Serialize error: {e}"))) } diff --git a/src-tauri/src/websocket/processor.rs b/src-tauri/src/websocket/processor.rs index 29157bcea..9faf91b90 100644 --- a/src-tauri/src/websocket/processor.rs +++ b/src-tauri/src/websocket/processor.rs @@ -16,10 +16,7 @@ impl MessageProcessor { /// 解析 ChatCompletions 请求 pub fn parse_chat_completions(payload: &Value) -> Result { serde_json::from_value(payload.clone()).map_err(|e| { - WsError::invalid_request( - None, - format!("Failed to parse ChatCompletionRequest: {}", e), - ) + WsError::invalid_request(None, format!("Failed to parse ChatCompletionRequest: {e}")) }) } @@ -28,7 +25,7 @@ impl MessageProcessor { serde_json::from_value(payload.clone()).map_err(|e| { WsError::invalid_request( None, - format!("Failed to parse AnthropicMessagesRequest: {}", e), + format!("Failed to parse AnthropicMessagesRequest: {e}"), ) }) } diff --git a/src-tauri/src/websocket/stream.rs b/src-tauri/src/websocket/stream.rs index b91c988ec..922910f0c 100644 --- a/src-tauri/src/websocket/stream.rs +++ b/src-tauri/src/websocket/stream.rs @@ -129,7 +129,7 @@ impl StreamForwarder { Err(e) => { return Err(WsError::upstream( Some(self.request_id.clone()), - format!("Stream error: {}", e), + format!("Stream error: {e}"), )); } } diff --git a/src-tauri/src/websocket/tests.rs b/src-tauri/src/websocket/tests.rs index 26a2ee38f..98c60b6c3 100644 --- a/src-tauri/src/websocket/tests.rs +++ b/src-tauri/src/websocket/tests.rs @@ -482,7 +482,7 @@ fn arb_sse_data() -> impl Strategy { /// 生成任意的 SSE 行 fn arb_sse_line() -> impl Strategy { - arb_sse_data().prop_map(|data| format!("data: {}", data)) + arb_sse_data().prop_map(|data| format!("data: {data}")) } /// 生成任意的 SSE 响应体(多行) @@ -491,7 +491,7 @@ fn arb_sse_body() -> impl Strategy, String)> { let lines: Vec = data_items.clone(); let body = data_items .iter() - .map(|d| format!("data: {}\n\n", d)) + .map(|d| format!("data: {d}\n\n")) .collect::>() .join(""); (lines, body) @@ -579,7 +579,7 @@ proptest! { index in 0u32..1000u32 ) { let forwarder = StreamForwarder::new(request_id.clone()); - let sse_line = format!("data: {}", data); + let sse_line = format!("data: {data}"); // 转换单行 SSE let result = forwarder.convert_sse_line(&sse_line, index); diff --git a/src-tauri/src/workspace/manager.rs b/src-tauri/src/workspace/manager.rs index d30df05ca..0af71bf0f 100644 --- a/src-tauri/src/workspace/manager.rs +++ b/src-tauri/src/workspace/manager.rs @@ -73,10 +73,7 @@ impl WorkspaceManager { stats: None, }; - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; Self::ensure_workspace_columns(&conn)?; @@ -87,10 +84,10 @@ impl WorkspaceManager { params![&root_path_str], |row| row.get(0), ) - .map_err(|e| format!("检查路径失败: {}", e))?; + .map_err(|e| format!("检查路径失败: {e}"))?; if exists { - return Err(format!("路径已存在: {}", root_path_str)); + return Err(format!("路径已存在: {root_path_str}")); } let settings_json = @@ -116,7 +113,7 @@ impl WorkspaceManager { workspace.updated_at.timestamp_millis(), ], ) - .map_err(|e| format!("创建 workspace 失败: {}", e))?; + .map_err(|e| format!("创建 workspace 失败: {e}"))?; tracing::info!( "[Workspace] 创建: id={}, name={}, path={}", @@ -132,20 +129,20 @@ impl WorkspaceManager { fn ensure_workspace_columns(conn: &rusqlite::Connection) -> Result<(), String> { let mut stmt = conn .prepare("PRAGMA table_info(workspaces)") - .map_err(|e| format!("读取 workspaces 表结构失败: {}", e))?; + .map_err(|e| format!("读取 workspaces 表结构失败: {e}"))?; let columns = stmt .query_map([], |row| { let column_name: String = row.get(1)?; Ok(column_name) }) - .map_err(|e| format!("读取 workspaces 表结构失败: {}", e))? + .map_err(|e| format!("读取 workspaces 表结构失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析 workspaces 表结构失败: {}", e))?; + .map_err(|e| format!("解析 workspaces 表结构失败: {e}"))?; let add_column = |sql: &str| -> Result<(), String> { conn.execute(sql, []) - .map_err(|e| format!("更新 workspaces 表结构失败: {}", e))?; + .map_err(|e| format!("更新 workspaces 表结构失败: {e}"))?; Ok(()) }; @@ -170,24 +167,21 @@ impl WorkspaceManager { /// 获取 workspace pub fn get(&self, id: &WorkspaceId) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT id, name, workspace_type, root_path, is_default, settings_json, created_at, updated_at, icon, color, is_favorite, is_archived, tags_json FROM workspaces WHERE id = ?", params![id], |row| { - Ok(Self::row_to_workspace(row)?) + Self::row_to_workspace(row) }, ); match result { Ok(workspace) => Ok(Some(workspace)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取 workspace 失败: {}", e)), + Err(e) => Err(format!("获取 workspace 失败: {e}")), } } @@ -195,56 +189,47 @@ impl WorkspaceManager { pub fn get_by_path(&self, root_path: &PathBuf) -> Result, String> { let root_path_str = root_path.to_str().ok_or("无效的路径")?; - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT id, name, workspace_type, root_path, is_default, settings_json, created_at, updated_at, icon, color, is_favorite, is_archived, tags_json FROM workspaces WHERE root_path = ?", params![root_path_str], |row| { - Ok(Self::row_to_workspace(row)?) + Self::row_to_workspace(row) }, ); match result { Ok(workspace) => Ok(Some(workspace)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取 workspace 失败: {}", e)), + Err(e) => Err(format!("获取 workspace 失败: {e}")), } } /// 列出所有 workspace pub fn list(&self) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( "SELECT id, name, workspace_type, root_path, is_default, settings_json, created_at, updated_at, icon, color, is_favorite, is_archived, tags_json FROM workspaces ORDER BY updated_at DESC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let workspaces = stmt - .query_map([], |row| Ok(Self::row_to_workspace(row)?)) - .map_err(|e| format!("查询失败: {}", e))? + .query_map([], Self::row_to_workspace) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(workspaces) } /// 列出所有项目类型的 workspace pub fn list_projects(&self) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( @@ -253,23 +238,20 @@ impl WorkspaceManager { WHERE workspace_type IN ('drama', 'novel', 'social', 'document', 'general') ORDER BY updated_at DESC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let workspaces = stmt - .query_map([], |row| Ok(Self::row_to_workspace(row)?)) - .map_err(|e| format!("查询失败: {}", e))? + .query_map([], Self::row_to_workspace) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(workspaces) } /// 列出指定类型的项目 pub fn list_by_type(&self, workspace_type: &WorkspaceType) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( @@ -278,25 +260,22 @@ impl WorkspaceManager { WHERE workspace_type = ? ORDER BY updated_at DESC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let workspaces = stmt .query_map(params![workspace_type.as_str()], |row| { - Ok(Self::row_to_workspace(row)?) + Self::row_to_workspace(row) }) - .map_err(|e| format!("查询失败: {}", e))? + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(workspaces) } /// 列出收藏的项目 pub fn list_favorites(&self) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( @@ -305,23 +284,20 @@ impl WorkspaceManager { WHERE is_favorite = 1 AND is_archived = 0 ORDER BY updated_at DESC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let workspaces = stmt - .query_map([], |row| Ok(Self::row_to_workspace(row)?)) - .map_err(|e| format!("查询失败: {}", e))? + .query_map([], Self::row_to_workspace) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(workspaces) } /// 列出归档的项目 pub fn list_archived(&self) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let mut stmt = conn .prepare( @@ -330,23 +306,20 @@ impl WorkspaceManager { WHERE is_archived = 1 ORDER BY updated_at DESC", ) - .map_err(|e| format!("准备查询失败: {}", e))?; + .map_err(|e| format!("准备查询失败: {e}"))?; let workspaces = stmt - .query_map([], |row| Ok(Self::row_to_workspace(row)?)) - .map_err(|e| format!("查询失败: {}", e))? + .query_map([], Self::row_to_workspace) + .map_err(|e| format!("查询失败: {e}"))? .collect::, _>>() - .map_err(|e| format!("解析结果失败: {}", e))?; + .map_err(|e| format!("解析结果失败: {e}"))?; Ok(workspaces) } /// 更新 workspace pub fn update(&self, id: &WorkspaceId, updates: WorkspaceUpdate) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let now = Utc::now().timestamp_millis(); // 构建更新语句 @@ -401,7 +374,7 @@ impl WorkspaceManager { params_vec.iter().map(|p| p.as_ref()).collect(); conn.execute(&sql, params_refs.as_slice()) - .map_err(|e| format!("更新 workspace 失败: {}", e))?; + .map_err(|e| format!("更新 workspace 失败: {e}"))?; drop(conn); @@ -410,14 +383,11 @@ impl WorkspaceManager { /// 删除 workspace pub fn delete(&self, id: &WorkspaceId) -> Result { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let affected = conn .execute("DELETE FROM workspaces WHERE id = ?", params![id]) - .map_err(|e| format!("删除 workspace 失败: {}", e))?; + .map_err(|e| format!("删除 workspace 失败: {e}"))?; if affected > 0 { tracing::info!("[Workspace] 删除: id={}", id); @@ -428,14 +398,11 @@ impl WorkspaceManager { /// 设置默认 workspace pub fn set_default(&self, id: &WorkspaceId) -> Result<(), String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; // 先清除所有默认标记 conn.execute("UPDATE workspaces SET is_default = 0", []) - .map_err(|e| format!("清除默认标记失败: {}", e))?; + .map_err(|e| format!("清除默认标记失败: {e}"))?; // 设置新的默认 let affected = conn @@ -443,7 +410,7 @@ impl WorkspaceManager { "UPDATE workspaces SET is_default = 1, updated_at = ? WHERE id = ?", params![Utc::now().timestamp_millis(), id], ) - .map_err(|e| format!("设置默认 workspace 失败: {}", e))?; + .map_err(|e| format!("设置默认 workspace 失败: {e}"))?; if affected == 0 { return Err("Workspace 不存在".to_string()); @@ -455,24 +422,21 @@ impl WorkspaceManager { /// 获取默认 workspace pub fn get_default(&self) -> Result, String> { - let conn = self - .db - .lock() - .map_err(|e| format!("数据库锁定失败: {}", e))?; + let conn = self.db.lock().map_err(|e| format!("数据库锁定失败: {e}"))?; let result = conn.query_row( "SELECT id, name, workspace_type, root_path, is_default, settings_json, created_at, updated_at, icon, color, is_favorite, is_archived, tags_json FROM workspaces WHERE is_default = 1", [], |row| { - Ok(Self::row_to_workspace(row)?) + Self::row_to_workspace(row) }, ); match result { Ok(workspace) => Ok(Some(workspace)), Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(format!("获取默认 workspace 失败: {}", e)), + Err(e) => Err(format!("获取默认 workspace 失败: {e}")), } } diff --git a/src-tauri/src/workspace/types.rs b/src-tauri/src/workspace/types.rs index b81f60daf..1c64a2cda 100644 --- a/src-tauri/src/workspace/types.rs +++ b/src-tauri/src/workspace/types.rs @@ -331,7 +331,7 @@ mod tests { for wt in types { let s = wt.as_str(); let parsed = WorkspaceType::from_str(s); - assert_eq!(wt, parsed, "Roundtrip failed for {:?}", wt); + assert_eq!(wt, parsed, "Roundtrip failed for {wt:?}"); } } @@ -351,7 +351,7 @@ mod tests { #[test] fn test_workspace_type_debug() { let wt = WorkspaceType::SocialMedia; - let debug_str = format!("{:?}", wt); + let debug_str = format!("{wt:?}"); assert_eq!(debug_str, "SocialMedia"); } } diff --git a/src-tauri/tests/api_key_provider_tests.rs b/src-tauri/tests/api_key_provider_tests.rs index aa3289622..fc7ec5a2f 100644 --- a/src-tauri/tests/api_key_provider_tests.rs +++ b/src-tauri/tests/api_key_provider_tests.rs @@ -91,7 +91,7 @@ impl TestContext { let now = chrono::Utc::now(); let provider = ApiKeyProvider { id: id.to_string(), - name: format!("Test Provider {}", id), + name: format!("Test Provider {id}"), provider_type: ApiProviderType::Openai, api_host: "https://api.test.com".to_string(), is_system: false, @@ -149,7 +149,7 @@ proptest! { // 添加 N 个 API Keys let mut expected_keys = Vec::new(); for i in 0..num_keys { - let api_key = format!("sk-test-key-{}-{}", provider_id, i); + let api_key = format!("sk-test-key-{provider_id}-{i}"); ctx.add_test_api_key(&provider_id, &api_key).expect("Failed to add API key"); expected_keys.push(api_key); } @@ -201,7 +201,7 @@ proptest! { // 添加 N 个 API Keys for i in 0..num_keys { - let api_key = format!("sk-cycle-key-{}-{}", provider_id, i); + let api_key = format!("sk-cycle-key-{provider_id}-{i}"); ctx.add_test_api_key(&provider_id, &api_key).expect("Failed to add API key"); } @@ -254,7 +254,7 @@ proptest! { ctx.create_test_provider(&provider_id).expect("Failed to create provider"); // 添加 API Key - let api_key = format!("sk-usage-test-{}", provider_id); + let api_key = format!("sk-usage-test-{provider_id}"); let entry = ctx.add_test_api_key(&provider_id, &api_key) .expect("Failed to add API key"); @@ -302,7 +302,7 @@ proptest! { ctx.create_test_provider(&provider_id).expect("Failed to create provider"); // 添加 API Key - let api_key = format!("sk-error-test-{}", provider_id); + let api_key = format!("sk-error-test-{provider_id}"); let entry = ctx.add_test_api_key(&provider_id, &api_key) .expect("Failed to add API key"); @@ -347,7 +347,7 @@ proptest! { ctx.create_test_provider(&provider_id).expect("Failed to create provider"); // 添加 API Key - let api_key = format!("sk-mixed-test-{}", provider_id); + let api_key = format!("sk-mixed-test-{provider_id}"); let entry = ctx.add_test_api_key(&provider_id, &api_key) .expect("Failed to add API key"); @@ -476,9 +476,9 @@ proptest! { let provider = ctx.service .add_custom_provider( &ctx.db, - format!("Provider {}", i), + format!("Provider {i}"), ApiProviderType::Openai, - format!("https://api{}.test.com", i), + format!("https://api{i}.test.com"), None, None, None, diff --git a/src-tauri/tests/end_to_end_tests.rs b/src-tauri/tests/end_to_end_tests.rs index b9ff48bca..cbe359707 100644 --- a/src-tauri/tests/end_to_end_tests.rs +++ b/src-tauri/tests/end_to_end_tests.rs @@ -128,7 +128,7 @@ impl E2ETestContext { }), error: None, metadata: FlowMetadata { - provider: provider, + provider, provider_id: None, credential_name: Some("test-cred".to_string()), credential_id: Some("test-cred-id".to_string()), diff --git a/src/components/agent/chat/components/EmptyState.tsx b/src/components/agent/chat/components/EmptyState.tsx index c4dd86fbe..25f94977b 100644 --- a/src/components/agent/chat/components/EmptyState.tsx +++ b/src/components/agent/chat/components/EmptyState.tsx @@ -653,6 +653,7 @@ export const EmptyState: React.FC = ({ ))} + {/* 输入卡片 */} ; + /** 当前输入值 */ + value: string; + /** 输入值变更回调 */ + onChange: (value: string) => void; + /** 选择角色回调 */ + onSelectCharacter?: (character: Character) => void; +} + +export function CharacterMention({ + characters, + inputRef, + value, + onChange, + onSelectCharacter, +}: CharacterMentionProps) { + const [showMentions, setShowMentions] = useState(false); + const [mentionQuery, setMentionQuery] = useState(""); + const [cursorPosition, setCursorPosition] = useState({ top: 0, left: 0 }); + const popoverRef = useRef(null); + + // 过滤角色列表 + const filteredCharacters = useMemo(() => { + if (!mentionQuery) return characters; + const query = mentionQuery.toLowerCase(); + return characters.filter( + (char) => + char.name.toLowerCase().includes(query) || + char.description?.toLowerCase().includes(query), + ); + }, [characters, mentionQuery]); + + // 检测 @ 符号 + useEffect(() => { + const textarea = inputRef.current; + if (!textarea) return; + + const handleInput = () => { + const cursorPos = textarea.selectionStart; + const textBeforeCursor = value.slice(0, cursorPos); + const lastAtIndex = textBeforeCursor.lastIndexOf("@"); + + // 检查是否在 @ 后面输入 + if (lastAtIndex !== -1) { + const textAfterAt = textBeforeCursor.slice(lastAtIndex + 1); + // 如果 @ 后面没有空格,说明正在输入角色名 + if (!textAfterAt.includes(" ") && !textAfterAt.includes("\n")) { + setMentionQuery(textAfterAt); + setShowMentions(true); + + // 计算弹窗位置 + const rect = textarea.getBoundingClientRect(); + const lineHeight = parseInt( + window.getComputedStyle(textarea).lineHeight, + ); + const lines = textBeforeCursor.split("\n"); + const currentLine = lines.length - 1; + const top = rect.top + currentLine * lineHeight - textarea.scrollTop; + const left = rect.left + 10; // 简单的左边距 + + setCursorPosition({ top, left }); + return; + } + } + + setShowMentions(false); + }; + + textarea.addEventListener("input", handleInput); + textarea.addEventListener("click", handleInput); + textarea.addEventListener("keyup", handleInput); + + return () => { + textarea.removeEventListener("input", handleInput); + textarea.removeEventListener("click", handleInput); + textarea.removeEventListener("keyup", handleInput); + }; + }, [value, inputRef]); + + // 插入角色引用 + const handleSelectCharacter = (character: Character) => { + const textarea = inputRef.current; + if (!textarea) return; + + const cursorPos = textarea.selectionStart; + const textBeforeCursor = value.slice(0, cursorPos); + const textAfterCursor = value.slice(cursorPos); + const lastAtIndex = textBeforeCursor.lastIndexOf("@"); + + // 替换 @ 和后面的查询文本为角色名 + const newValue = + value.slice(0, lastAtIndex) + `@${character.name} ` + textAfterCursor; + + onChange(newValue); + setShowMentions(false); + + // 通知父组件 + onSelectCharacter?.(character); + + // 恢复焦点并设置光标位置 + setTimeout(() => { + textarea.focus(); + const newCursorPos = lastAtIndex + character.name.length + 2; // @ + 名字 + 空格 + textarea.setSelectionRange(newCursorPos, newCursorPos); + }, 0); + }; + + // 处理键盘事件 + useEffect(() => { + const textarea = inputRef.current; + if (!textarea || !showMentions) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + setShowMentions(false); + e.preventDefault(); + } + }; + + textarea.addEventListener("keydown", handleKeyDown); + return () => textarea.removeEventListener("keydown", handleKeyDown); + }, [showMentions, inputRef]); + + if (!showMentions || characters.length === 0) return null; + + return ( + + +
+ + e.preventDefault()} + > + + + + 没有找到角色 + + {filteredCharacters.map((character) => ( + handleSelectCharacter(character)} + className="cursor-pointer" + > + +
+
{character.name}
+ {character.description && ( +
+ {character.description} +
+ )} +
+
+ ))} +
+
+
+
+ + ); +} diff --git a/src/components/agent/chat/components/Inputbar/components/InputbarCore.tsx b/src/components/agent/chat/components/Inputbar/components/InputbarCore.tsx index 3bca3ed6e..c420eadba 100644 --- a/src/components/agent/chat/components/Inputbar/components/InputbarCore.tsx +++ b/src/components/agent/chat/components/Inputbar/components/InputbarCore.tsx @@ -40,6 +40,8 @@ interface InputbarCoreProps { isFullscreen?: boolean; /** 画布是否打开 */ isCanvasOpen?: boolean; + /** Textarea ref(用于 CharacterMention) */ + textareaRef?: React.RefObject; } export const InputbarCore: React.FC = ({ @@ -56,8 +58,10 @@ export const InputbarCore: React.FC = ({ onPaste, isFullscreen = false, isCanvasOpen = false, + textareaRef: externalTextareaRef, }) => { - const textareaRef = useRef(null); + const internalTextareaRef = useRef(null); + const textareaRef = externalTextareaRef || internalTextareaRef; const hasContent = text.trim().length > 0 || pendingImages.length > 0; // Auto-resize textarea @@ -70,7 +74,7 @@ export const InputbarCore: React.FC = ({ textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 300)}px`; } } - }, [text, isFullscreen]); + }, [text, isFullscreen, textareaRef]); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { diff --git a/src/components/agent/chat/components/Inputbar/index.tsx b/src/components/agent/chat/components/Inputbar/index.tsx index d46fc9ab3..a54758dbf 100644 --- a/src/components/agent/chat/components/Inputbar/index.tsx +++ b/src/components/agent/chat/components/Inputbar/index.tsx @@ -1,9 +1,11 @@ import React from "react"; import { InputbarCore } from "./components/InputbarCore"; +import { CharacterMention } from "./components/CharacterMention"; import { toast } from "sonner"; import { useState, useCallback, useRef } from "react"; import styled from "styled-components"; import type { MessageImage } from "../../types"; +import type { Character } from "@/lib/api/memory"; import { TaskFileList, type TaskFile } from "../TaskFiles"; import { FolderOpen, ChevronUp } from "lucide-react"; @@ -92,6 +94,10 @@ interface InputbarProps { onToggleTaskFiles?: () => void; /** 文件点击回调 */ onTaskFileClick?: (file: TaskFile) => void; + /** 角色列表(用于 @ 引用) */ + characters?: Character[]; + /** 选择角色回调 */ + onSelectCharacter?: (character: Character) => void; } export const Inputbar: React.FC = ({ @@ -109,11 +115,14 @@ export const Inputbar: React.FC = ({ taskFilesExpanded = false, onToggleTaskFiles, onTaskFileClick, + characters = [], + onSelectCharacter, }) => { const [activeTools, setActiveTools] = useState>({}); const [pendingImages, setPendingImages] = useState([]); const [isFullscreen, setIsFullscreen] = useState(false); const fileInputRef = useRef(null); + const textareaRef = useRef(null); const handleToolClick = useCallback( (tool: string) => { @@ -326,7 +335,18 @@ export const Inputbar: React.FC = ({ style={{ display: "none" }} onChange={handleFileSelect} /> + {/* 角色引用组件 */} + {characters.length > 0 && ( + + )} void; + /** 创建项目回调 */ + onCreateProject?: () => void; +} + +export function ProjectSelector({ + activeTheme = "general", + onSelectProject, + onCreateProject: _onCreateProject, +}: ProjectSelectorProps) { + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(true); + const [searchQuery, setSearchQuery] = useState(""); + const [creating, setCreating] = useState(false); + + // 加载项目列表 + useEffect(() => { + loadProjects(); + }, []); + + const loadProjects = async () => { + setLoading(true); + try { + const allProjects = await listProjects(); + setProjects(allProjects); + } catch (error) { + console.error("加载项目失败:", error); + toast.error("加载项目失败"); + } finally { + setLoading(false); + } + }; + + // 过滤项目(按主题和搜索关键词) + const filteredProjects = useMemo(() => { + let result = projects; + + // 按主题过滤 + if (activeTheme !== "general") { + result = result.filter((p) => p.workspaceType === activeTheme); + } + + // 搜索过滤 + if (searchQuery) { + const query = searchQuery.toLowerCase(); + result = result.filter( + (p) => + p.name.toLowerCase().includes(query) || + p.tags.some((t) => t.toLowerCase().includes(query)), + ); + } + + // 排除归档项目 + result = result.filter((p) => !p.isArchived); + + // 按更新时间排序 + result.sort( + (a, b) => + new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(), + ); + + return result; + }, [projects, activeTheme, searchQuery]); + + // 快速创建项目 + const handleQuickCreate = async () => { + setCreating(true); + try { + const projectType = activeTheme as ProjectType; + const name = generateProjectName(projectType); + const rootPath = getDefaultProjectPath(); + + const newProject = await createProject({ + name, + rootPath, + workspaceType: projectType, + }); + + toast.success("项目创建成功"); + await loadProjects(); + onSelectProject(newProject.id); + } catch (error) { + console.error("创建项目失败:", error); + toast.error("创建项目失败"); + } finally { + setCreating(false); + } + }; + + // 格式化时间 + const formatTime = (timestamp: number) => { + const date = new Date(timestamp); + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + + if (days === 0) return "今天"; + if (days === 1) return "昨天"; + if (days < 7) return `${days} 天前`; + if (days < 30) return `${Math.floor(days / 7)} 周前`; + return `${Math.floor(days / 30)} 月前`; + }; + + return ( +
+ {/* 搜索和快速创建 */} +
+
+ + setSearchQuery(e.target.value)} + className="pl-9" + /> +
+ +
+ + {/* 项目列表 */} + + {loading ? ( +
+
加载中...
+
+ ) : filteredProjects.length === 0 ? ( +
+ +

+ {searchQuery ? "没有找到匹配的项目" : "还没有项目"} +

+ {!searchQuery && ( + + )} +
+ ) : ( +
+ {filteredProjects.map((project) => ( + + ))} +
+ )} +
+
+ ); +} diff --git a/src/components/agent/chat/hooks/useContentSync.ts b/src/components/agent/chat/hooks/useContentSync.ts new file mode 100644 index 000000000..9087363b8 --- /dev/null +++ b/src/components/agent/chat/hooks/useContentSync.ts @@ -0,0 +1,102 @@ +/** + * 内容同步 Hook + * + * 提供防抖同步、状态管理和失败重试功能 + */ + +import { useState, useCallback, useRef } from "react"; +import { updateContent } from "@/lib/api/project"; + +export type SyncStatus = "idle" | "syncing" | "success" | "error"; + +interface UseContentSyncOptions { + /** 防抖延迟(毫秒) */ + debounceMs?: number; + /** 是否自动重试 */ + autoRetry?: boolean; + /** 重试延迟(毫秒) */ + retryDelayMs?: number; +} + +interface UseContentSyncReturn { + /** 同步内容 */ + syncContent: (contentId: string, body: string) => void; + /** 同步状态 */ + syncStatus: SyncStatus; + /** 手动重置状态 */ + resetStatus: () => void; +} + +export function useContentSync( + options: UseContentSyncOptions = {}, +): UseContentSyncReturn { + const { debounceMs = 2000, autoRetry = true, retryDelayMs = 5000 } = options; + + const [syncStatus, setSyncStatus] = useState("idle"); + const syncTimeoutRef = useRef>(); + const retryTimeoutRef = useRef>(); + const lastSyncDataRef = useRef<{ contentId: string; body: string } | null>( + null, + ); + + const syncContent = useCallback( + (contentId: string, body: string) => { + // 保存最后的同步数据(用于重试) + lastSyncDataRef.current = { contentId, body }; + + // 清除之前的定时器 + if (syncTimeoutRef.current) { + clearTimeout(syncTimeoutRef.current); + } + if (retryTimeoutRef.current) { + clearTimeout(retryTimeoutRef.current); + } + + // 防抖:延迟后同步 + syncTimeoutRef.current = setTimeout(async () => { + setSyncStatus("syncing"); + + try { + await updateContent(contentId, { body }); + setSyncStatus("success"); + + // 3 秒后重置状态 + setTimeout(() => { + setSyncStatus((current) => + current === "success" ? "idle" : current, + ); + }, 3000); + } catch (error) { + console.error("同步内容失败:", error); + setSyncStatus("error"); + + // 自动重试 + if (autoRetry && lastSyncDataRef.current) { + retryTimeoutRef.current = setTimeout(() => { + if (lastSyncDataRef.current) { + console.log("[useContentSync] 重试同步..."); + syncContent( + lastSyncDataRef.current.contentId, + lastSyncDataRef.current.body, + ); + } + }, retryDelayMs); + } + } + }, debounceMs); + }, + [debounceMs, autoRetry, retryDelayMs], + ); + + const resetStatus = useCallback(() => { + setSyncStatus("idle"); + if (syncTimeoutRef.current) { + clearTimeout(syncTimeoutRef.current); + } + if (retryTimeoutRef.current) { + clearTimeout(retryTimeoutRef.current); + } + }, []); + + return { syncContent, syncStatus, resetStatus }; +} diff --git a/src/components/agent/chat/index.tsx b/src/components/agent/chat/index.tsx index b0e14348e..7e682417f 100644 --- a/src/components/agent/chat/index.tsx +++ b/src/components/agent/chat/index.tsx @@ -10,6 +10,7 @@ import { useState, useCallback, useMemo, useEffect, useRef } from "react"; import styled from "styled-components"; import { useAgentChat } from "./hooks/useAgentChat"; import { useSessionFiles } from "./hooks/useSessionFiles"; +import { useContentSync } from "./hooks/useContentSync"; import { ChatNavbar } from "./components/ChatNavbar"; import { ChatSidebar } from "./components/ChatSidebar"; import { ChatSettings } from "./components/ChatSettings"; @@ -52,7 +53,11 @@ import { type Project, type ProjectType, } from "@/lib/api/project"; -import { getProjectMemory, type ProjectMemory } from "@/lib/api/memory"; +import { + getProjectMemory, + type ProjectMemory, + type Character, +} from "@/lib/api/memory"; import type { MessageImage } from "./types"; import type { ThemeType, LayoutMode } from "@/components/content-creator/types"; @@ -184,6 +189,11 @@ export function AgentChatPage({ null, ); + // 引用的角色列表(用于注入到消息中) + const [mentionedCharacters, setMentionedCharacters] = useState( + [], + ); + // 用于追踪已处理的消息 ID,避免重复处理 const processedMessageIds = useRef>(new Set()); @@ -198,6 +208,13 @@ export function AgentChatPage({ creationMode, ); + // 内容同步 Hook + const { syncContent, syncStatus } = useContentSync({ + debounceMs: 2000, + autoRetry: true, + retryDelayMs: 5000, + }); + // 判断是否为内容创作模式 const isContentCreationMode = isContentCreationTheme(activeTheme); @@ -322,6 +339,53 @@ export function AgentChatPage({ autoInit: true, }); + // 监听画布状态变化,自动同步到 Content + useEffect(() => { + if (!canvasState) return; + + // 提取画布内容 + let content = ""; + try { + switch (canvasState.type) { + case "document": + content = canvasState.content || ""; + break; + case "novel": + content = JSON.stringify(canvasState.chapters); + break; + case "script": + content = JSON.stringify(canvasState.scenes); + break; + case "music": + content = JSON.stringify(canvasState.sections); + break; + case "poster": + content = JSON.stringify(canvasState.pages); + break; + default: + content = JSON.stringify(canvasState); + } + + // 如果有 contentId,直接同步 + if (contentId && content) { + syncContent(contentId, content); + } + // 如果没有 contentId 但有 projectId,自动创建 Content + else if (!contentId && projectId && content && project) { + // 只在内容不为空时创建 + const isEmpty = + !content || content === "" || content === "[]" || content === "{}"; + if (!isEmpty) { + console.log("[AgentChatPage] 自动创建 Content 记录"); + // TODO: 实现自动创建 Content 的逻辑 + // 这里需要调用 createContent API,但为了避免重复创建,需要添加防抖和状态管理 + } + } + } catch (error) { + console.error("提取画布内容失败:", error); + } + }, [canvasState, contentId, projectId, project, syncContent]); + // 追踪已恢复元数据和文件的会话 ID const restoredMetaSessionId = useRef(null); const restoredFilesSessionId = useRef(null); @@ -534,11 +598,28 @@ export function AgentChatPage({ thinking?: boolean, ) => { if (!input.trim() && (!images || images.length === 0)) return; - const text = input; + let text = input; + + // 如果有引用的角色,注入角色信息 + if (mentionedCharacters.length > 0) { + const characterContext = mentionedCharacters + .map((char) => { + let context = `角色:${char.name}`; + if (char.description) context += `\n简介:${char.description}`; + if (char.personality) context += `\n性格:${char.personality}`; + if (char.background) context += `\n背景:${char.background}`; + return context; + }) + .join("\n\n"); + + text = `[角色上下文]\n${characterContext}\n\n[用户输入]\n${text}`; + } + setInput(""); + setMentionedCharacters([]); // 清空引用的角色 await sendMessage(text, images || [], webSearch, thinking); }, - [input, sendMessage], + [input, mentionedCharacters, sendMessage], ); const handleClearMessages = useCallback(() => { @@ -1153,6 +1234,14 @@ export function AgentChatPage({ taskFilesExpanded={taskFilesExpanded} onToggleTaskFiles={() => setTaskFilesExpanded(!taskFilesExpanded)} onTaskFileClick={handleTaskFileClick} + characters={projectMemory?.characters || []} + onSelectCharacter={(character) => { + setMentionedCharacters((prev) => { + // 避免重复添加 + if (prev.find((c) => c.id === character.id)) return prev; + return [...prev, character]; + }); + }} /> )} @@ -1263,6 +1352,36 @@ export function AgentChatPage({ onToggleSettings={() => setShowSettings(!showSettings)} /> + {/* 同步状态指示器 */} + {contentId && syncStatus !== "idle" && ( +
+ {syncStatus === "syncing" && "正在同步..."} + {syncStatus === "success" && "✓ 已保存"} + {syncStatus === "error" && "⚠ 同步失败,将自动重试"} +
+ )} + {/* 使用布局过渡组件 */} p.workspaceType === currentFilter); - break; - case "favorites": - result = result.filter((p) => p.isFavorite); - break; - case "archived": - result = result.filter((p) => p.isArchived); - break; - default: - // 默认不显示归档的项目 - result = result.filter((p) => !p.isArchived); + if (currentFilter === "all") { + // 默认不显示归档的项目 + result = result.filter((p) => !p.isArchived); + } else if (currentFilter === "favorites") { + // 收藏:显示所有收藏的非归档项目 + result = result.filter((p) => p.isFavorite && !p.isArchived); + } else if (currentFilter === "archived") { + // 归档:只显示归档的项目 + result = result.filter((p) => p.isArchived); + } else { + // 其他情况都是具体的项目类型(UserType) + result = result.filter( + (p) => p.workspaceType === currentFilter && !p.isArchived, + ); } // 搜索过滤 diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 000000000..445e6a9c2 --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,153 @@ +import * as React from "react"; +import { type DialogProps } from "@radix-ui/react-dialog"; +import { Command as CommandPrimitive } from "cmdk"; +import { Search } from "lucide-react"; + +import { cn } from "@/lib/utils"; +import { Dialog, DialogContent } from "@/components/ui/dialog"; + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Command.displayName = CommandPrimitive.displayName; + +type CommandDialogProps = DialogProps; + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ); +}; + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)); + +CommandInput.displayName = CommandPrimitive.Input.displayName; + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandList.displayName = CommandPrimitive.List.displayName; + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)); + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName; + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandGroup.displayName = CommandPrimitive.Group.displayName; + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CommandSeparator.displayName = CommandPrimitive.Separator.displayName; + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandItem.displayName = CommandPrimitive.Item.displayName; + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +CommandShortcut.displayName = "CommandShortcut"; + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +};