From d2c2b43c63e83f7c27378b62e4d5cf743bf9c538 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 3 Sep 2026 18:00:17 +0000 Subject: [PATCH] chore(dx): enforce clippy and rustfmt in CI CI now fails on `cargo fmt --check` and `cargo clippy --locked --all-targets -- -D warnings`. The live baseline is fixed so the gate is green. `&PathBuf` params become `&Path`, `catalog_lookup_enabled` uses `matches!`, `term` uses `div_ceil`, `persist_channel` uses `unwrap_or_default`, `ToolConfig` derives Default, and the help thread_local initializer is const. The JS-mirrored test helper `getKeyPrefixStyleMask` keeps its name behind `#[allow(non_snake_case)]`. Closes #22 Co-authored-by: Duyet Le Co-authored-by: duyetbot --- .github/workflows/ci.yml | 8 +++-- src/commands.rs | 68 +++++++++++++++++++--------------------- src/help.rs | 2 +- src/http.rs | 1 + src/spawn.rs | 19 ++--------- src/term.rs | 2 +- src/tui/view.rs | 2 +- src/upgrade.rs | 4 +-- 8 files changed, 45 insertions(+), 61 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ccc3cc..2c6d7e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,13 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, llvm-tools-preview + components: rustfmt, clippy, llvm-tools-preview - - name: cargo fmt --check (optional) + - name: cargo fmt --check run: cargo fmt --check - continue-on-error: true + + - name: cargo clippy + run: cargo clippy --locked --all-targets -- -D warnings - uses: taiki-e/install-action@v2 with: diff --git a/src/commands.rs b/src/commands.rs index 636d5d1..920c31d 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,5 +1,5 @@ use std::collections::{BTreeMap, HashMap}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use crate::auth::acquire_api_key; @@ -946,7 +946,7 @@ fn known_model_id(models: &[CatalogModel], id: &str) -> bool { fn save_model_slot( existing: Option, - path: &PathBuf, + path: &Path, slot: &str, id: &str, ) -> Result { @@ -988,7 +988,7 @@ fn known_agent(name: &str) -> Result { } } -fn save_agent_model(path: &PathBuf, agent: &str, id: &str) -> Result { +fn save_agent_model(path: &Path, agent: &str, id: &str) -> Result { let agent = known_agent(agent)?; let mut cfg = load_config_if_present(path).ok_or_else(no_key_error)?; let id = catalog_model_id(id); @@ -1008,7 +1008,7 @@ fn save_agent_model(path: &PathBuf, agent: &str, id: &str) -> Result Result { +fn save_agent_account(path: &Path, agent: &str, profile: &str) -> Result { let agent = known_agent(agent)?; let mut cfg = load_config_if_present(path).ok_or_else(no_key_error)?; if !cfg.profiles.contains_key(profile) { @@ -1024,7 +1024,7 @@ fn save_agent_account(path: &PathBuf, agent: &str, profile: &str) -> Result Result { +fn save_agent_key(path: &Path, agent: &str, key: &str) -> Result { let agent = known_agent(agent)?; let mut cfg = load_config_if_present(path).ok_or_else(no_key_error)?; cfg.agent_binding_mut(&agent).api_key = Some(key.to_string()); @@ -1323,7 +1323,7 @@ fn run_whoami(parsed: &ParsedArgs, env: &BTreeMap) -> Result, - path: &PathBuf, + path: &Path, ) -> Result<(), String> { let cfg = load_config_if_present(path).unwrap_or_default(); let profile = cfg.profiles.get(&cfg.active_profile); @@ -1469,7 +1469,7 @@ fn settings_tab_names() -> Vec { tabs } -fn tool_command_for(path: &PathBuf, id: &str) -> String { +fn tool_command_for(path: &Path, id: &str) -> String { let cfg = load_config_if_present(path); resolve_tool(cfg.as_ref(), id) .map(|t| t.command) @@ -1480,7 +1480,7 @@ fn tool_command_for(path: &PathBuf, id: &str) -> String { fn config_settings_frame( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, online: bool, cache: &mut CreditsCache, tab: usize, @@ -1553,7 +1553,7 @@ fn config_settings_frame( fn fill_general_settings( rows: &mut Vec, kinds: &mut Vec>, - path: &PathBuf, + path: &Path, parsed: &ParsedArgs, env: &BTreeMap, profile: Option<&Profile>, @@ -1689,7 +1689,7 @@ fn fill_general_settings( fn fill_agent_settings( rows: &mut Vec, kinds: &mut Vec>, - path: &PathBuf, + path: &Path, env: &BTreeMap, profile: Option<&Profile>, id: &'static str, @@ -1982,7 +1982,7 @@ fn slot_current_opt(profile: Option<&Profile>, slot: &str) -> String { fn config_settings_loop( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, ) -> Result { let mut cache = CreditsCache::fresh(); let mut tab = 0usize; @@ -2029,7 +2029,7 @@ fn config_settings_loop( fn config_edit_row( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, kind: SettingKind, ) -> Result { match kind { @@ -2406,7 +2406,7 @@ fn config_reset_row(path: &std::path::Path, kind: SettingKind) -> Result, - path: &PathBuf, + path: &Path, ) -> Result { let items = vec![ "Switch key".into(), @@ -2535,10 +2535,10 @@ fn run_config(parsed: &ParsedArgs, env: &BTreeMap) -> Result) -> bool { - match env.get("ANYR_NO_CATALOG").map(|s| s.as_str()) { - Some("1" | "true" | "TRUE" | "yes") => false, - _ => true, - } + !matches!( + env.get("ANYR_NO_CATALOG").map(|s| s.as_str()), + Some("1" | "true" | "TRUE" | "yes") + ) } struct ResolvedModel { @@ -3138,7 +3138,7 @@ fn key_pick_label(row: &crate::http::RemoteKey, current: bool) -> String { fn stored_api_key( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, ) -> Option { let existing = load_config_if_present(path); let profile = existing @@ -3196,7 +3196,7 @@ fn agent_binding_detail(cfg: &crate::config::Config, id: &str, signed_in: bool) /// `signed_in` gates the launch group exactly like the old dialog did. #[cfg(feature = "native")] fn launcher_palette( - path: &PathBuf, + path: &Path, parsed: &ParsedArgs, env: &BTreeMap, credits: &mut CreditsCache, @@ -3273,7 +3273,7 @@ fn launcher_palette( /// Non-native twin of `launcher_palette` — same rows, plain inline entries. #[cfg(not(feature = "native"))] fn launcher_palette( - path: &PathBuf, + path: &Path, parsed: &ParsedArgs, env: &BTreeMap, _credits: &mut CreditsCache, @@ -3561,7 +3561,7 @@ fn push_agent_configure_entries( )); } -fn install_agent_dialog(path: &PathBuf, env: &BTreeMap) -> Result { +fn install_agent_dialog(path: &Path, env: &BTreeMap) -> Result { let missing = missing_agents(env, |id| tool_command_for(path, id)); if missing.is_empty() { println!( @@ -3593,7 +3593,7 @@ fn install_agent_dialog(path: &PathBuf, env: &BTreeMap) -> Resul Ok(0) } -fn persist_tool_command(path: &PathBuf, id: &str, command: &str) -> Result<(), String> { +fn persist_tool_command(path: &Path, id: &str, command: &str) -> Result<(), String> { let mut cfg = load_config_if_present(path).unwrap_or_default(); let mut tool = resolve_tool(Some(&cfg), id)?; tool.command = command.to_string(); @@ -3601,11 +3601,7 @@ fn persist_tool_command(path: &PathBuf, id: &str, command: &str) -> Result<(), S write_config(&cfg, path) } -fn launcher_last_tool( - path: &PathBuf, - parsed: &ParsedArgs, - env: &BTreeMap, -) -> String { +fn launcher_last_tool(path: &Path, parsed: &ParsedArgs, env: &BTreeMap) -> String { let cfg = load_config_if_present(path).unwrap_or_default(); let profile = cfg.profiles.get(&cfg.active_profile); cfg.last_tool @@ -3620,7 +3616,7 @@ fn launcher_last_tool( }) } -fn launcher_signed_in(path: &PathBuf, parsed: &ParsedArgs, env: &BTreeMap) -> bool { +fn launcher_signed_in(path: &Path, parsed: &ParsedArgs, env: &BTreeMap) -> bool { stored_api_key(parsed, env, path).is_some() } @@ -3741,7 +3737,7 @@ fn launcher_dispatch( action: &str, parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, ) -> Result { if action == "Quit" || action.starts_with("Quit") { return Ok(LauncherNext::Exit(0)); @@ -3868,7 +3864,7 @@ fn launcher_dispatch( fn switch_agent_model( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, agent: &str, ) -> Result { if agent.is_empty() { @@ -3889,7 +3885,7 @@ fn switch_agent_model( fn bind_agent_model( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, agent: &str, ) -> Result { let existing = load_config_if_present(path); @@ -3911,7 +3907,7 @@ fn bind_agent_model( fn switch_agent_account( _parsed: &ParsedArgs, _env: &BTreeMap, - path: &PathBuf, + path: &Path, agent: &str, ) -> Result { if agent.is_empty() { @@ -3925,7 +3921,7 @@ fn switch_agent_account( Ok(LauncherNext::Continue) } -fn bind_agent_account(path: &PathBuf, agent: &str) -> Result { +fn bind_agent_account(path: &Path, agent: &str) -> Result { let cfg = load_config_if_present(path).unwrap_or_default(); let mut names: Vec = cfg.profiles.keys().cloned().collect(); names.sort(); @@ -3960,7 +3956,7 @@ fn bind_agent_account(path: &PathBuf, agent: &str) -> Result { fn switch_agent_key( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, agent: &str, ) -> Result { if agent.is_empty() { @@ -3981,7 +3977,7 @@ fn switch_agent_key( fn bind_agent_key( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, agent: &str, ) -> Result { let (_keys_path, cfg, base, api_key) = keys_credential(parsed, env)?; @@ -4024,7 +4020,7 @@ fn bind_agent_key( fn launch_agent_picker( parsed: &ParsedArgs, env: &BTreeMap, - path: &PathBuf, + path: &Path, ) -> Result { if !launcher_signed_in(path, parsed, env) { eprintln!( diff --git a/src/help.rs b/src/help.rs index cc64784..db435b2 100644 --- a/src/help.rs +++ b/src/help.rs @@ -4,7 +4,7 @@ use std::path::Path; use crate::VERSION; thread_local! { - static INVOKED_BIN: RefCell = RefCell::new(String::new()); + static INVOKED_BIN: RefCell = const { RefCell::new(String::new()) }; } const LAUNCH_HELP_BODY: &str = "\ diff --git a/src/http.rs b/src/http.rs index 13e05a7..d88877f 100644 --- a/src/http.rs +++ b/src/http.rs @@ -722,6 +722,7 @@ mod tests { } /// Helper mirroring the server's getKeyPrefix() shape for tests. + #[allow(non_snake_case)] fn getKeyPrefixStyleMask(head: &str, tail: &str) -> String { format!("{head}...{tail}") } diff --git a/src/spawn.rs b/src/spawn.rs index 8ba4f3d..58983ae 100644 --- a/src/spawn.rs +++ b/src/spawn.rs @@ -22,7 +22,7 @@ const CLAUDE_EFFORT_TOKENS: &[(&str, i64)] = &[ ("max", 32000), ]; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct ToolConfig { pub command: String, pub base_url_env: String, @@ -34,21 +34,6 @@ pub struct ToolConfig { pub extra: BTreeMap, } -impl Default for ToolConfig { - fn default() -> Self { - Self { - command: String::new(), - base_url_env: String::new(), - auth_env: String::new(), - model_env: None, - base_suffix: String::new(), - enable_gateway_model_discovery: false, - shadow_env: None, - extra: BTreeMap::new(), - } - } -} - impl ToolConfig { pub fn merge(&mut self, over: &ToolConfig) { if !over.command.is_empty() { @@ -1177,7 +1162,7 @@ mod tests { let mut env = BTreeMap::new(); let mut routing = crate::config::RoutingConstraints::default(); apply_routing_env(&mut env, &routing, "claude"); - assert!(env.get("CLAUDE_CODE_EXTRA_BODY").is_none()); + assert!(!env.contains_key("CLAUDE_CODE_EXTRA_BODY")); routing.set_exacto(true); routing.set_require_tools(true); routing.set_require_1m(true); diff --git a/src/term.rs b/src/term.rs index 14b6a5f..7cc5824 100644 --- a/src/term.rs +++ b/src/term.rs @@ -80,7 +80,7 @@ const MARK_PNG: &[u8] = include_bytes!("../assets/mark.png"); const B64: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; fn base64_encode(bytes: &[u8]) -> String { - let mut out = String::with_capacity((bytes.len() + 2) / 3 * 4); + let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4); for chunk in bytes.chunks(3) { let a = chunk[0] as u32; let b = chunk.get(1).copied().unwrap_or(0) as u32; diff --git a/src/tui/view.rs b/src/tui/view.rs index f64e586..ba325cf 100644 --- a/src/tui/view.rs +++ b/src/tui/view.rs @@ -69,7 +69,7 @@ pub fn render_picker(frame: &mut Frame, state: &PickerState) { ListItem::new(Line::from(vec![ Span::styled(if i == state.cursor { "❯ " } else { " " }, theme::accent()), Span::styled(item_icon(label), item_icon_style(label)), - Span::styled(format!("{label}"), style), + Span::styled(label.to_string(), style), ])) }) .collect(); diff --git a/src/upgrade.rs b/src/upgrade.rs index 23c3e5a..a49aadf 100644 --- a/src/upgrade.rs +++ b/src/upgrade.rs @@ -15,7 +15,7 @@ use crate::channel::{ parse_releases_html, release_asset_url, releases_http_error, select_latest_release_with_asset, Channel, Release, GITHUB_EXPANDED_ASSETS_PREFIX, GITHUB_RELEASES_API, GITHUB_RELEASES_HTML, }; -use crate::config::{resolve_config_path, write_config, Config}; +use crate::config::{resolve_config_path, write_config}; use crate::http::{http_get_github, http_get_web}; use crate::key::load_config_if_present; use crate::parse::{get_string_flag, ParsedArgs}; @@ -180,7 +180,7 @@ fn resolve_channel(parsed: &ParsedArgs, env: &BTreeMap) -> Resul /// Persist `channel:` so future auto-updates follow the switched track. fn persist_channel(channel: Channel, env: &BTreeMap) -> Result { let path = resolve_config_path(None, env); - let mut cfg = load_config_if_present(&path).unwrap_or_else(Config::default); + let mut cfg = load_config_if_present(&path).unwrap_or_default(); let next = channel.as_str().to_string(); let changed = cfg.channel.as_deref() != Some(next.as_str()); if !changed && cfg.channel.is_some() {