diff --git a/src-tauri/src/acp/connection.rs b/src-tauri/src/acp/connection.rs index 8bcca6f..7a947c1 100644 --- a/src-tauri/src/acp/connection.rs +++ b/src-tauri/src/acp/connection.rs @@ -8,17 +8,16 @@ use sacp::schema::{ CreateTerminalRequest, CreateTerminalResponse, EmbeddedResource, EmbeddedResourceResource, FileSystemCapability, ImageContent, InitializeRequest, KillTerminalCommandRequest, KillTerminalCommandResponse, LoadSessionRequest, NewSessionRequest, NewSessionResponse, - PermissionOptionKind, Plan, PlanEntryPriority, PlanEntryStatus, PromptRequest, - ProtocolVersion, ReadTextFileRequest, ReadTextFileResponse, ReleaseTerminalRequest, - ReleaseTerminalResponse, RequestPermissionOutcome, RequestPermissionRequest, - RequestPermissionResponse, ResourceLink, SelectedPermissionOutcome, SessionConfigKind, - SessionConfigOption, SessionConfigOptionCategory, SessionConfigSelectGroup, - SessionConfigSelectOption, SessionConfigSelectOptions, SessionId, SessionModeState, - SessionNotification, SessionUpdate, SetSessionConfigOptionRequest, + PermissionOptionKind, Plan, PlanEntryPriority, PlanEntryStatus, PromptRequest, ProtocolVersion, + ReadTextFileRequest, ReadTextFileResponse, ReleaseTerminalRequest, ReleaseTerminalResponse, + RequestPermissionOutcome, RequestPermissionRequest, RequestPermissionResponse, ResourceLink, + SelectedPermissionOutcome, SessionConfigKind, SessionConfigOption, SessionConfigOptionCategory, + SessionConfigSelectGroup, SessionConfigSelectOption, SessionConfigSelectOptions, SessionId, + SessionModeState, SessionNotification, SessionUpdate, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, SetSessionModeRequest, StopReason, TerminalExitStatus, TerminalOutputRequest, TerminalOutputResponse, TextContent, TextResourceContents, - ToolCallContent, WaitForTerminalExitRequest, WaitForTerminalExitResponse, - WriteTextFileRequest, WriteTextFileResponse, + ToolCallContent, WaitForTerminalExitRequest, WaitForTerminalExitResponse, WriteTextFileRequest, + WriteTextFileResponse, }; use sacp::util::MatchDispatch; use sacp::{ @@ -790,7 +789,7 @@ fn respond_terminal_request( ) -> Result<(), sacp::Error> { match result { Ok(response) => responder.respond(response), - Err(error) => responder.respond_with_error(error.to_rpc_error()), + Err(error) => responder.respond_with_error(error.into_rpc_error()), } } @@ -800,7 +799,7 @@ fn respond_file_system_request( ) -> Result<(), sacp::Error> { match result { Ok(response) => responder.respond(response), - Err(error) => responder.respond_with_error(error.to_rpc_error()), + Err(error) => responder.respond_with_error(error.into_rpc_error()), } } @@ -1176,7 +1175,8 @@ fn map_prompt_blocks(blocks: Vec) -> Vec { EmbeddedResourceResource::BlobResourceContents(content) } (None, None) => { - let content = TextResourceContents::new("", uri.clone()).mime_type(mime_type); + let content = + TextResourceContents::new("", uri.clone()).mime_type(mime_type); EmbeddedResourceResource::TextResourceContents(content) } }; diff --git a/src-tauri/src/acp/file_system_runtime.rs b/src-tauri/src/acp/file_system_runtime.rs index 767d86a..4bf93da 100644 --- a/src-tauri/src/acp/file_system_runtime.rs +++ b/src-tauri/src/acp/file_system_runtime.rs @@ -23,7 +23,7 @@ pub enum FileSystemRuntimeError { } impl FileSystemRuntimeError { - pub fn to_rpc_error(self) -> sacp::Error { + pub fn into_rpc_error(self) -> sacp::Error { match self { Self::InvalidParams(message) => sacp::Error::invalid_params().data(message), Self::Internal(message) => sacp::util::internal_error(message), diff --git a/src-tauri/src/acp/terminal_runtime.rs b/src-tauri/src/acp/terminal_runtime.rs index e02831f..3435588 100644 --- a/src-tauri/src/acp/terminal_runtime.rs +++ b/src-tauri/src/acp/terminal_runtime.rs @@ -21,7 +21,7 @@ pub enum TerminalRuntimeError { } impl TerminalRuntimeError { - pub fn to_rpc_error(self) -> sacp::Error { + pub fn into_rpc_error(self) -> sacp::Error { match self { Self::InvalidParams(message) => sacp::Error::invalid_params().data(message), Self::Internal(message) => sacp::util::internal_error(message), diff --git a/src-tauri/src/commands/acp.rs b/src-tauri/src/commands/acp.rs index 3a4785d..89cc3aa 100644 --- a/src-tauri/src/commands/acp.rs +++ b/src-tauri/src/commands/acp.rs @@ -1126,7 +1126,7 @@ fn list_skills_from_dir( } by_id.insert( id.clone(), - build_skill_item(id, scope.clone(), AgentSkillLayout::SkillDirectory, path), + build_skill_item(id, scope, AgentSkillLayout::SkillDirectory, path), ); continue; } @@ -1145,7 +1145,7 @@ fn list_skills_from_dir( } by_id.insert( stem.clone(), - build_skill_item(stem, scope.clone(), AgentSkillLayout::MarkdownFile, path), + build_skill_item(stem, scope, AgentSkillLayout::MarkdownFile, path), ); } } @@ -1167,7 +1167,7 @@ fn locate_existing_skill( if skill_dir.is_dir() && skill_dir.join("SKILL.md").is_file() { return Some(build_skill_item( skill_id.to_string(), - scope.clone(), + scope, AgentSkillLayout::SkillDirectory, skill_dir, )); @@ -1542,6 +1542,7 @@ pub async fn acp_clear_binary_cache(agent_type: AgentType) -> Result<(), AcpErro } #[tauri::command] +#[allow(clippy::too_many_arguments)] pub async fn acp_update_agent_preferences( agent_type: AgentType, enabled: bool, @@ -1923,8 +1924,8 @@ pub async fn acp_list_agent_skills( let mut skills = skills_by_key.into_values().collect::>(); skills.sort_by(|a, b| { - scope_rank(a.scope.clone()) - .cmp(&scope_rank(b.scope.clone())) + scope_rank(a.scope) + .cmp(&scope_rank(b.scope)) .then_with(|| a.name.cmp(&b.name)) }); @@ -1953,7 +1954,7 @@ pub async fn acp_read_agent_skill( let skill = locate_existing_skill_across_dirs(&dirs, spec.kind, &id, scope) .ok_or_else(|| AcpError::protocol(format!("skill not found: {id}")))?; - let content_path = skill_content_path(skill.layout.clone(), Path::new(&skill.path)); + let content_path = skill_content_path(skill.layout, Path::new(&skill.path)); let content = fs::read_to_string(&content_path) .map_err(|e| AcpError::protocol(format!("failed to read skill content: {e}")))?; Ok(AgentSkillContent { skill, content }) @@ -1998,7 +1999,7 @@ pub async fn acp_save_agent_skill( }; let skill_path = PathBuf::from(&skill.path); - let content_path = skill_content_path(skill.layout.clone(), &skill_path); + let content_path = skill_content_path(skill.layout, &skill_path); if skill.layout == AgentSkillLayout::SkillDirectory { fs::create_dir_all(&skill_path).map_err(|e| { diff --git a/src-tauri/src/commands/mcp.rs b/src-tauri/src/commands/mcp.rs index 4ebaec2..521ed23 100644 --- a/src-tauri/src/commands/mcp.rs +++ b/src-tauri/src/commands/mcp.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use std::time::Duration; use serde::de::DeserializeOwned; @@ -11,6 +12,14 @@ use crate::app_error::AppCommandError; const MARKETPLACE_OFFICIAL: &str = "official_registry"; const MARKETPLACE_SMITHERY: &str = "smithery"; +static MARKETPLACE_HTTP_CLIENT: LazyLock> = LazyLock::new(|| { + reqwest::Client::builder() + .connect_timeout(Duration::from_secs(8)) + .timeout(Duration::from_secs(20)) + .user_agent("codeg-mcp-market/1.0") + .build() + .map_err(|e| format!("failed to initialize marketplace HTTP client: {e}")) +}); fn mcp_invalid_input(message: impl Into) -> AppCommandError { AppCommandError::invalid_input(message) @@ -588,11 +597,9 @@ fn write_codex_root_toml(root: &toml::Value) -> Result<(), AppCommandError> { } fn obj_as_string_map(value: Option<&Value>) -> Option> { - let Some(obj) = value.and_then(Value::as_object) else { - return None; - }; + let obj = value.and_then(Value::as_object)?; - let mut output = Map::new(); + let mut output = Map::with_capacity(obj.len()); for (key, item) in obj { let Some(s) = item.as_str() else { continue; @@ -616,12 +623,10 @@ fn contains_unresolved_placeholder(value: &str) -> bool { } fn marketplace_http_client() -> Result { - reqwest::Client::builder() - .connect_timeout(Duration::from_secs(8)) - .timeout(Duration::from_secs(20)) - .user_agent("codeg-mcp-market/1.0") - .build() - .map_err(|e| mcp_network(format!("failed to initialize marketplace HTTP client: {e}"))) + match &*MARKETPLACE_HTTP_CLIENT { + Ok(client) => Ok(client.clone()), + Err(err) => Err(mcp_network(err.clone())), + } } fn should_retry_http_status(status: reqwest::StatusCode) -> bool { @@ -1000,11 +1005,9 @@ fn json_to_toml_value(value: &Value) -> Option { } Value::String(v) => Some(toml::Value::String(v.clone())), Value::Array(values) => { - let mut converted = Vec::new(); + let mut converted = Vec::with_capacity(values.len()); for item in values { - let Some(next) = json_to_toml_value(item) else { - return None; - }; + let next = json_to_toml_value(item)?; converted.push(next); } Some(toml::Value::Array(converted)) diff --git a/src-tauri/src/commands/windows.rs b/src-tauri/src/commands/windows.rs index 70726c0..a1144d8 100644 --- a/src-tauri/src/commands/windows.rs +++ b/src-tauri/src/commands/windows.rs @@ -29,9 +29,9 @@ where { #[cfg(target_os = "macos")] { - return builder + builder .hidden_title(true) - .title_bar_style(tauri::TitleBarStyle::Overlay); + .title_bar_style(tauri::TitleBarStyle::Overlay) } #[cfg(target_os = "windows")] @@ -216,9 +216,9 @@ pub async fn open_folder_window( if let Some(existing) = app.get_webview_window(&label) { ensure_windows_undecorated(&existing); let _ = existing.unminimize(); - existing.set_focus().map_err(|e| { - AppCommandError::window("Failed to focus folder window", e.to_string()) - })?; + existing + .set_focus() + .map_err(|e| AppCommandError::window("Failed to focus folder window", e.to_string()))?; if let Some(w) = app.get_webview_window("welcome") { let _ = w.close(); } @@ -279,7 +279,7 @@ pub async fn open_commit_window( let url = WebviewUrl::App(format!("commit?folderId={folder_id}").into()); let builder = WebviewWindowBuilder::new(&app, &label, url) - .title(&format!("提交代码 - {}", folder.name)) + .title(format!("提交代码 - {}", folder.name)) .inner_size(1220.0, 820.0) .min_inner_size(980.0, 620.0) .always_on_top(true) diff --git a/src-tauri/src/db/service/agent_setting_service.rs b/src-tauri/src/db/service/agent_setting_service.rs index acfdf60..3f4dd14 100644 --- a/src-tauri/src/db/service/agent_setting_service.rs +++ b/src-tauri/src/db/service/agent_setting_service.rs @@ -194,7 +194,7 @@ async fn reorder_once(conn: &DatabaseConnection, agent_types: &[AgentType]) -> R } let mut active = model.into_active_model(); active.sort_order = Set(index as i32); - active.updated_at = Set(now.clone()); + active.updated_at = Set(now); active.update(conn).await?; } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f88fa08..eca0a8b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -97,7 +97,7 @@ pub fn run() { { let app = window.app_handle(); if let Some(state) = app.try_state::() { - windows::restore_windows_after_settings(&app, &state); + windows::restore_windows_after_settings(app, &state); } } @@ -109,7 +109,7 @@ pub fn run() { { let app = window.app_handle(); if let Some(state) = app.try_state::() { - windows::restore_window_after_commit(&app, &state, &label); + windows::restore_window_after_commit(app, &state, &label); } } diff --git a/src-tauri/src/parsers/codex.rs b/src-tauri/src/parsers/codex.rs index be8da6a..8b2ee1d 100644 --- a/src-tauri/src/parsers/codex.rs +++ b/src-tauri/src/parsers/codex.rs @@ -246,16 +246,21 @@ fn value_to_preview(value: Option<&serde_json::Value>) -> Option { } fn is_failed_status(status: &str) -> bool { - matches!( - status.to_ascii_lowercase().as_str(), - "error" | "failed" | "failure" | "cancelled" | "canceled" - ) + let status = status.trim(); + status.eq_ignore_ascii_case("error") + || status.eq_ignore_ascii_case("failed") + || status.eq_ignore_ascii_case("failure") + || status.eq_ignore_ascii_case("cancelled") + || status.eq_ignore_ascii_case("canceled") } fn parse_nonzero_exit_code_from_line(line: &str) -> Option { - let lower = line.trim().to_ascii_lowercase(); - let rest = lower.strip_prefix("exit code:")?; - let number_text = rest.trim().split_whitespace().next()?; + let trimmed = line.trim(); + let (label, rest) = trimmed.split_once(':')?; + if !label.trim_end().eq_ignore_ascii_case("exit code") { + return None; + } + let number_text = rest.split_whitespace().next()?; let code = number_text.parse::().ok()?; if code == 0 { None @@ -298,7 +303,9 @@ fn infer_output_text_is_error(text: &str) -> bool { return true; } - trimmed.to_ascii_lowercase().starts_with("error:") + trimmed + .get(..6) + .is_some_and(|prefix| prefix.eq_ignore_ascii_case("error:")) } fn infer_output_value_is_error(value: &serde_json::Value, depth: usize) -> bool { diff --git a/src-tauri/src/parsers/gemini.rs b/src-tauri/src/parsers/gemini.rs index f37aa40..4b9414a 100644 --- a/src-tauri/src/parsers/gemini.rs +++ b/src-tauri/src/parsers/gemini.rs @@ -101,7 +101,7 @@ impl GeminiParser { let projects = value.get("projects")?.as_object()?; projects .iter() - .find_map(|(path, mapped_alias)| (mapped_alias.as_str() == Some(alias)).then(|| path)) + .find_map(|(path, mapped_alias)| (mapped_alias.as_str() == Some(alias)).then_some(path)) .map(|s| s.to_string()) }