diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 1502e62f2f..b407621624 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -37,6 +37,38 @@ expect_used = "deny" redundant_clone = "deny" uninlined_format_args = "deny" unwrap_used = "deny" +manual_clamp = "deny" +manual_filter = "deny" +manual_find = "deny" +manual_flatten = "deny" +manual_map = "deny" +manual_memcpy = "deny" +manual_non_exhaustive = "deny" +manual_ok_or = "deny" +manual_range_contains = "deny" +manual_retain = "deny" +manual_strip = "deny" +manual_try_fold = "deny" +manual_unwrap_or = "deny" +redundant_closure = "deny" +redundant_closure_for_method_calls = "deny" +redundant_pattern_matching = "deny" +redundant_static_lifetimes = "deny" +redundant_else = "deny" +needless_borrow = "deny" +needless_borrowed_reference = "deny" +needless_collect = "deny" +needless_option_as_deref = "deny" +needless_update = "deny" +needless_pass_by_value = "deny" +needless_late_init = "deny" +needless_question_mark = "deny" +trivially_copy_pass_by_ref = "deny" +identity_op = "deny" +unnecessary_lazy_evaluations = "deny" +unnecessary_sort_by = "deny" +unnecessary_filter_map = "deny" +unnecessary_to_owned = "deny" [profile.release] lto = "fat" diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs index 5bda31c4c2..445d9a0875 100644 --- a/codex-rs/apply-patch/src/lib.rs +++ b/codex-rs/apply-patch/src/lib.rs @@ -650,19 +650,22 @@ fn derive_new_contents_from_chunks( let mut original_lines: Vec = original_contents .split('\n') - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .collect(); // Drop the trailing empty element that results from the final newline so // that line counts match the behaviour of standard `diff`. - if original_lines.last().is_some_and(|s| s.is_empty()) { + if original_lines + .last() + .is_some_and(std::string::String::is_empty) + { original_lines.pop(); } let replacements = compute_replacements(&original_lines, path, chunks)?; let new_lines = apply_replacements(original_lines, &replacements); let mut new_lines = new_lines; - if !new_lines.last().is_some_and(|s| s.is_empty()) { + if !new_lines.last().is_some_and(std::string::String::is_empty) { new_lines.push(String::new()); } let new_contents = new_lines.join("\n"); @@ -706,7 +709,10 @@ fn compute_replacements( if chunk.old_lines.is_empty() { // Pure addition (no old lines). We'll add them at the end or just // before the final empty line if one exists. - let insertion_idx = if original_lines.last().is_some_and(|s| s.is_empty()) { + let insertion_idx = if original_lines + .last() + .is_some_and(std::string::String::is_empty) + { original_lines.len() - 1 } else { original_lines.len() @@ -732,11 +738,11 @@ fn compute_replacements( let mut new_slice: &[String] = &chunk.new_lines; - if found.is_none() && pattern.last().is_some_and(|s| s.is_empty()) { + if found.is_none() && pattern.last().is_some_and(std::string::String::is_empty) { // Retry without the trailing empty line which represents the final // newline in the file. pattern = &pattern[..pattern.len() - 1]; - if new_slice.last().is_some_and(|s| s.is_empty()) { + if new_slice.last().is_some_and(std::string::String::is_empty) { new_slice = &new_slice[..new_slice.len() - 1]; } @@ -856,7 +862,7 @@ mod tests { } fn strs_to_strings(strs: &[&str]) -> Vec { - strs.iter().map(|s| s.to_string()).collect() + strs.iter().map(std::string::ToString::to_string).collect() } // Test helpers to reduce repetition when building bash -lc heredoc scripts diff --git a/codex-rs/apply-patch/src/parser.rs b/codex-rs/apply-patch/src/parser.rs index 768c89ad78..ebfd56f391 100644 --- a/codex-rs/apply-patch/src/parser.rs +++ b/codex-rs/apply-patch/src/parser.rs @@ -112,6 +112,7 @@ pub fn parse_patch(patch: &str) -> Result { parse_patch_text(patch, mode) } +#[derive(Clone, Copy, Debug, PartialEq)] enum ParseMode { /// Parse the patch text argument as is. Strict, diff --git a/codex-rs/apply-patch/src/seek_sequence.rs b/codex-rs/apply-patch/src/seek_sequence.rs index 0144580f9b..b7802ed5ea 100644 --- a/codex-rs/apply-patch/src/seek_sequence.rs +++ b/codex-rs/apply-patch/src/seek_sequence.rs @@ -114,7 +114,10 @@ mod tests { use super::seek_sequence; fn to_vec(strings: &[&str]) -> Vec { - strings.iter().map(|s| s.to_string()).collect() + strings + .iter() + .map(std::string::ToString::to_string) + .collect() } #[test] diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs index d5df68e554..865d006fac 100644 --- a/codex-rs/arg0/src/lib.rs +++ b/codex-rs/arg0/src/lib.rs @@ -54,7 +54,9 @@ where let argv1 = args.next().unwrap_or_default(); if argv1 == CODEX_APPLY_PATCH_ARG1 { - let patch_arg = args.next().and_then(|s| s.to_str().map(|s| s.to_owned())); + let patch_arg = args + .next() + .and_then(|s| s.to_str().map(std::borrow::ToOwned::to_owned)); let exit_code = match patch_arg { Some(patch_arg) => { let mut stdout = std::io::stdout(); diff --git a/codex-rs/cli/src/login.rs b/codex-rs/cli/src/login.rs index f0816d0b29..ee02ed86f4 100644 --- a/codex-rs/cli/src/login.rs +++ b/codex-rs/cli/src/login.rs @@ -23,7 +23,7 @@ pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> { } pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! { - let config = load_config_or_exit(cli_config_overrides); + let config = load_config_or_exit(&cli_config_overrides); match login_with_chatgpt(config.codex_home).await { Ok(_) => { @@ -41,7 +41,7 @@ pub async fn run_login_with_api_key( cli_config_overrides: CliConfigOverrides, api_key: String, ) -> ! { - let config = load_config_or_exit(cli_config_overrides); + let config = load_config_or_exit(&cli_config_overrides); match login_with_api_key(&config.codex_home, &api_key) { Ok(_) => { @@ -56,7 +56,7 @@ pub async fn run_login_with_api_key( } pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! { - let config = load_config_or_exit(cli_config_overrides); + let config = load_config_or_exit(&cli_config_overrides); match CodexAuth::from_codex_home(&config.codex_home) { Ok(Some(auth)) => match auth.mode { @@ -87,7 +87,7 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! { } pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! { - let config = load_config_or_exit(cli_config_overrides); + let config = load_config_or_exit(&cli_config_overrides); match logout(&config.codex_home) { Ok(true) => { @@ -105,7 +105,7 @@ pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! { } } -fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config { +fn load_config_or_exit(cli_config_overrides: &CliConfigOverrides) -> Config { let cli_overrides = match cli_config_overrides.parse_overrides() { Ok(v) => v, Err(e) => { diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index e66855fe73..de3e787d7f 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -243,7 +243,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() proto::run_main(proto_cli).await?; } Some(Subcommand::Completion(completion_cli)) => { - print_completion(completion_cli); + print_completion(&completion_cli); } Some(Subcommand::Debug(debug_args)) => match debug_args.cmd { DebugCommand::Seatbelt(mut seatbelt_cli) => { @@ -363,7 +363,7 @@ fn merge_resume_cli_flags(interactive: &mut TuiCli, resume_cli: TuiCli) { .extend(resume_cli.config_overrides.raw_overrides); } -fn print_completion(cmd: CompletionCommand) { +fn print_completion(cmd: &CompletionCommand) { let mut app = MultitoolCli::command(); let name = "codex"; generate(cmd.shell, &mut app, name, &mut std::io::stdout()); diff --git a/codex-rs/cli/src/mcp_cmd.rs b/codex-rs/cli/src/mcp_cmd.rs index 437511ad57..caf5488f8a 100644 --- a/codex-rs/cli/src/mcp_cmd.rs +++ b/codex-rs/cli/src/mcp_cmd.rs @@ -99,10 +99,10 @@ impl McpCli { codex_mcp_server::run_main(codex_linux_sandbox_exe, config_overrides).await?; } McpSubcommand::List(args) => { - run_list(&config_overrides, args)?; + run_list(&config_overrides, &args)?; } McpSubcommand::Get(args) => { - run_get(&config_overrides, args)?; + run_get(&config_overrides, &args)?; } McpSubcommand::Add(args) => { run_add(&config_overrides, args)?; @@ -188,7 +188,7 @@ fn run_remove(config_overrides: &CliConfigOverrides, remove_args: RemoveArgs) -> Ok(()) } -fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) -> Result<()> { +fn run_list(config_overrides: &CliConfigOverrides, list_args: &ListArgs) -> Result<()> { let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?; let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default()) .context("failed to load configuration")?; @@ -285,7 +285,7 @@ fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) -> Resul Ok(()) } -fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Result<()> { +fn run_get(config_overrides: &CliConfigOverrides, get_args: &GetArgs) -> Result<()> { let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?; let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default()) .context("failed to load configuration")?; diff --git a/codex-rs/core/src/auth.rs b/codex-rs/core/src/auth.rs index a2158310dc..5c98f3ea16 100644 --- a/codex-rs/core/src/auth.rs +++ b/codex-rs/core/src/auth.rs @@ -392,7 +392,7 @@ mod tests { async fn roundtrip_auth_dot_json() { let codex_home = tempdir().unwrap(); let _ = write_auth_file( - AuthFileParams { + &AuthFileParams { openai_api_key: None, chatgpt_plan_type: "pro".to_string(), }, @@ -438,7 +438,7 @@ mod tests { async fn pro_account_with_no_api_key_uses_chatgpt_auth() { let codex_home = tempdir().unwrap(); let fake_jwt = write_auth_file( - AuthFileParams { + &AuthFileParams { openai_api_key: None, chatgpt_plan_type: "pro".to_string(), }, @@ -519,7 +519,7 @@ mod tests { chatgpt_plan_type: String, } - fn write_auth_file(params: AuthFileParams, codex_home: &Path) -> std::io::Result { + fn write_auth_file(params: &AuthFileParams, codex_home: &Path) -> std::io::Result { let auth_file = get_auth_file(codex_home); // Create a minimal valid JWT for the id_token field. #[derive(Serialize)] @@ -536,7 +536,7 @@ mod tests { "email_verified": true, "https://api.openai.com/auth": { "chatgpt_account_id": "bc3618e3-489d-4d49-9362-1561dc53ba53", - "chatgpt_plan_type": params.chatgpt_plan_type, + "chatgpt_plan_type": params.chatgpt_plan_type.clone(), "chatgpt_user_id": "user-12345", "user_id": "user-12345", } @@ -548,7 +548,7 @@ mod tests { let fake_jwt = format!("{header_b64}.{payload_b64}.{signature_b64}"); let auth_json_data = json!({ - "OPENAI_API_KEY": params.openai_api_key, + "OPENAI_API_KEY": params.openai_api_key.clone(), "tokens": { "id_token": fake_jwt, "access_token": "test-access-token", diff --git a/codex-rs/core/src/bash.rs b/codex-rs/core/src/bash.rs index 4bed3a9c16..593d65dce2 100644 --- a/codex-rs/core/src/bash.rs +++ b/codex-rs/core/src/bash.rs @@ -74,7 +74,7 @@ pub fn try_parse_word_only_commands_sequence(tree: &Tree, src: &str) -> Option( if let Some(reasoning_val) = choice.get("delta").and_then(|d| d.get("reasoning")) { let mut maybe_text = reasoning_val .as_str() - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .filter(|s| !s.is_empty()); if maybe_text.is_none() && reasoning_val.is_object() { @@ -706,9 +706,8 @@ where return Poll::Ready(Some(Ok(ResponseEvent::OutputItemDone( item, )))); - } else { - continue; } + continue; } } } @@ -792,9 +791,8 @@ where if matches!(this.mode, AggregateMode::Streaming) { // In streaming mode, also forward the delta immediately. return Poll::Ready(Some(Ok(ResponseEvent::OutputTextDelta(delta)))); - } else { - continue; } + continue; } Poll::Ready(Some(Ok(ResponseEvent::ReasoningContentDelta(delta)))) => { // Always accumulate reasoning deltas so we can emit a final Reasoning item at Completed. @@ -802,9 +800,8 @@ where if matches!(this.mode, AggregateMode::Streaming) { // In streaming mode, also forward the delta immediately. return Poll::Ready(Some(Ok(ResponseEvent::ReasoningContentDelta(delta)))); - } else { - continue; } + continue; } Poll::Ready(Some(Ok(ResponseEvent::ReasoningSummaryDelta(_)))) => { continue; diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 57848d388b..56e1a11602 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -335,9 +335,10 @@ impl ModelClient { // Prefer the plan_type provided in the error message if present // because it's more up to date than the one encoded in the auth // token. - let plan_type = error - .plan_type - .or_else(|| auth.as_ref().and_then(|a| a.get_plan_type())); + let plan_type = error.plan_type.or_else(|| { + auth.as_ref() + .and_then(super::auth::CodexAuth::get_plan_type) + }); let resets_in_seconds = error.resets_in_seconds; return Err(CodexErr::UsageLimitReached(UsageLimitReachedError { plan_type, diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 05ef09377e..047d8eb653 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -306,9 +306,8 @@ pub(crate) struct TurnContext { } impl TurnContext { - fn resolve_path(&self, path: Option) -> PathBuf { - path.as_ref() - .map(PathBuf::from) + fn resolve_path(&self, path: Option<&str>) -> PathBuf { + path.map(PathBuf::from) .map_or_else(|| self.cwd.clone(), |p| self.cwd.join(p)) } } @@ -1038,7 +1037,7 @@ impl Session { /// Spawn the configured notifier (if any) with the given JSON payload as /// the last argument. Failures are logged but otherwise ignored so that /// notification issues do not interfere with the main workflow. - fn maybe_notify(&self, notification: UserNotification) { + fn maybe_notify(&self, notification: &UserNotification) { let Some(notify_command) = &self.notify else { return; }; @@ -1047,7 +1046,7 @@ impl Session { return; } - let Ok(json) = serde_json::to_string(¬ification) else { + let Ok(json) = serde_json::to_string(notification) else { error!("failed to serialise notification payload"); return; }; @@ -1104,14 +1103,14 @@ pub(crate) struct AgentTask { impl AgentTask { fn spawn( sess: Arc, - turn_context: Arc, + turn_context: &Arc, sub_id: String, input: Vec, ) -> Self { let handle = { let sess = sess.clone(); let sub_id = sub_id.clone(); - let tc = Arc::clone(&turn_context); + let tc = Arc::clone(turn_context); tokio::spawn(async move { run_task(sess, tc, sub_id, input).await }).abort_handle() }; Self { @@ -1124,14 +1123,14 @@ impl AgentTask { fn review( sess: Arc, - turn_context: Arc, + turn_context: &Arc, sub_id: String, input: Vec, ) -> Self { let handle = { let sess = sess.clone(); let sub_id = sub_id.clone(); - let tc = Arc::clone(&turn_context); + let tc = Arc::clone(turn_context); tokio::spawn(async move { run_task(sess, tc, sub_id, input).await }).abort_handle() }; Self { @@ -1144,7 +1143,7 @@ impl AgentTask { fn compact( sess: Arc, - turn_context: Arc, + turn_context: &Arc, sub_id: String, input: Vec, compact_instructions: String, @@ -1152,7 +1151,7 @@ impl AgentTask { let handle = { let sess = sess.clone(); let sub_id = sub_id.clone(); - let tc = Arc::clone(&turn_context); + let tc = Arc::clone(turn_context); tokio::spawn(async move { compact::run_compact_task(sess, tc, sub_id, input, compact_instructions).await }) @@ -1291,8 +1290,7 @@ async fn submission_loop( // attempt to inject input into current task if let Err(items) = sess.inject_input(items).await { // no current task, spawn a new one - let task = - AgentTask::spawn(sess.clone(), Arc::clone(&turn_context), sub.id, items); + let task = AgentTask::spawn(sess.clone(), &turn_context, sub.id, items); sess.set_task(task).await; } } @@ -1368,8 +1366,7 @@ async fn submission_loop( turn_context = Arc::new(fresh_turn_context); // no current task, spawn a new one with the per‑turn context - let task = - AgentTask::spawn(sess.clone(), Arc::clone(&turn_context), sub.id, items); + let task = AgentTask::spawn(sess.clone(), &turn_context, sub.id, items); sess.set_task(task).await; } } @@ -1618,7 +1615,7 @@ async fn spawn_review_thread( // Clone sub_id for the upcoming announcement before moving it into the task. let sub_id_for_event = sub_id.clone(); - let task = AgentTask::review(sess.clone(), tc.clone(), sub_id, input); + let task = AgentTask::review(sess.clone(), &tc, sub_id, input); sess.set_task(task).await; // Announce entering review mode so UIs can switch modes. @@ -1748,7 +1745,7 @@ async fn run_task( .unwrap_or(i64::MAX); let total_usage_tokens = total_token_usage .as_ref() - .map(|usage| usage.tokens_in_context_window()); + .map(codex_protocol::protocol::TokenUsage::tokens_in_context_window); let token_limit_reached = total_usage_tokens .map(|tokens| (tokens as i64) >= limit) .unwrap_or(false); @@ -1884,7 +1881,7 @@ async fn run_task( last_agent_message = get_last_assistant_message_from_turn( &items_to_record_in_conversation_history, ); - sess.maybe_notify(UserNotification::AgentTurnComplete { + sess.maybe_notify(&UserNotification::AgentTurnComplete { turn_id: sub_id.clone(), input_messages: turn_input_messages, last_assistant_message: last_agent_message.clone(), @@ -2254,7 +2251,7 @@ async fn handle_response_item( turn_diff_tracker, sub_id.to_string(), name, - arguments, + &arguments, call_id, ) .await, @@ -2427,7 +2424,7 @@ async fn handle_function_call( turn_diff_tracker: &mut TurnDiffTracker, sub_id: String, name: String, - arguments: String, + arguments: &str, call_id: String, ) -> ResponseInputItem { match name.as_str() { @@ -2458,7 +2455,7 @@ async fn handle_function_call( timeout_ms: Option, } - let args = match serde_json::from_str::(&arguments) { + let args = match serde_json::from_str::(arguments) { Ok(args) => args, Err(err) => { return ResponseInputItem::FunctionCallOutput { @@ -2485,7 +2482,7 @@ async fn handle_function_call( struct SeeImageArgs { path: String, } - let args = match serde_json::from_str::(&arguments) { + let args = match serde_json::from_str::(arguments) { Ok(a) => a, Err(e) => { return ResponseInputItem::FunctionCallOutput { @@ -2497,7 +2494,7 @@ async fn handle_function_call( }; } }; - let abs = turn_context.resolve_path(Some(args.path)); + let abs = turn_context.resolve_path(Some(&args.path)); let output = match sess .inject_input(vec![InputItem::LocalImage { path: abs }]) .await @@ -2514,7 +2511,7 @@ async fn handle_function_call( ResponseInputItem::FunctionCallOutput { call_id, output } } "apply_patch" => { - let args = match serde_json::from_str::(&arguments) { + let args = match serde_json::from_str::(arguments) { Ok(a) => a, Err(e) => { return ResponseInputItem::FunctionCallOutput { @@ -2547,7 +2544,7 @@ async fn handle_function_call( "update_plan" => handle_update_plan(sess, arguments, sub_id, call_id).await, EXEC_COMMAND_TOOL_NAME => { // TODO(mbolin): Sandbox check. - let exec_params = match serde_json::from_str::(&arguments) { + let exec_params = match serde_json::from_str::(arguments) { Ok(params) => params, Err(e) => { return ResponseInputItem::FunctionCallOutput { @@ -2570,7 +2567,7 @@ async fn handle_function_call( } } WRITE_STDIN_TOOL_NAME => { - let write_stdin_params = match serde_json::from_str::(&arguments) { + let write_stdin_params = match serde_json::from_str::(arguments) { Ok(params) => params, Err(e) => { return ResponseInputItem::FunctionCallOutput { @@ -2599,7 +2596,13 @@ async fn handle_function_call( // TODO(mbolin): Determine appropriate timeout for tool call. let timeout = None; handle_mcp_tool_call( - sess, &sub_id, call_id, server, tool_name, arguments, timeout, + sess, + &sub_id, + call_id, + server, + tool_name, + arguments.to_string(), + timeout, ) .await } @@ -2673,7 +2676,7 @@ async fn handle_custom_tool_call( fn to_exec_params(params: ShellToolCallParams, turn_context: &TurnContext) -> ExecParams { ExecParams { command: params.command, - cwd: turn_context.resolve_path(params.workdir.clone()), + cwd: turn_context.resolve_path(params.workdir.as_deref()), timeout_ms: params.timeout_ms, env: create_env(&turn_context.shell_environment_policy), with_escalated_permissions: params.with_escalated_permissions, @@ -2682,12 +2685,12 @@ fn to_exec_params(params: ShellToolCallParams, turn_context: &TurnContext) -> Ex } fn parse_container_exec_arguments( - arguments: String, + arguments: &str, turn_context: &TurnContext, call_id: &str, ) -> Result> { // parse command - match serde_json::from_str::(&arguments) { + match serde_json::from_str::(arguments) { Ok(shell_tool_call_params) => Ok(to_exec_params(shell_tool_call_params, turn_context)), Err(e) => { // allow model to re-sample diff --git a/codex-rs/core/src/codex/compact.rs b/codex-rs/core/src/codex/compact.rs index 05d57cd945..6307471dcb 100644 --- a/codex-rs/core/src/codex/compact.rs +++ b/codex-rs/core/src/codex/compact.rs @@ -44,7 +44,7 @@ pub(super) async fn spawn_compact_task( ) { let task = AgentTask::compact( sess.clone(), - turn_context, + &turn_context, sub_id, input, SUMMARIZATION_PROMPT.to_string(), @@ -159,16 +159,15 @@ async fn run_compact_task_inner( .await; tokio::time::sleep(delay).await; continue; - } else { - let event = Event { - id: sub_id.clone(), - msg: EventMsg::Error(ErrorEvent { - message: e.to_string(), - }), - }; - sess.send_event(event).await; - return; } + let event = Event { + id: sub_id.clone(), + msg: EventMsg::Error(ErrorEvent { + message: e.to_string(), + }), + }; + sess.send_event(event).await; + return; } } } diff --git a/codex-rs/core/src/config_edit.rs b/codex-rs/core/src/config_edit.rs index 4b3a5cbdb0..fedbcc2a54 100644 --- a/codex-rs/core/src/config_edit.rs +++ b/codex-rs/core/src/config_edit.rs @@ -136,7 +136,7 @@ async fn persist_overrides_with_behavior( } else { doc.get("profile") .and_then(|i| i.as_str()) - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) }; let mut mutated = false; diff --git a/codex-rs/core/src/conversation_manager.rs b/codex-rs/core/src/conversation_manager.rs index 51854248a4..0dece647ee 100644 --- a/codex-rs/core/src/conversation_manager.rs +++ b/codex-rs/core/src/conversation_manager.rs @@ -148,7 +148,7 @@ impl ConversationManager { ) -> CodexResult { // Compute the prefix up to the cut point. let history = RolloutRecorder::get_rollout_history(&path).await?; - let history = truncate_before_nth_user_message(history, nth_user_message); + let history = truncate_before_nth_user_message(&history, nth_user_message); // Spawn a new conversation with the computed initial history. let auth_manager = self.auth_manager.clone(); @@ -163,7 +163,7 @@ impl ConversationManager { /// Return a prefix of `items` obtained by cutting strictly before the nth user message /// (0-based) and all items that follow it. -fn truncate_before_nth_user_message(history: InitialHistory, n: usize) -> InitialHistory { +fn truncate_before_nth_user_message(history: &InitialHistory, n: usize) -> InitialHistory { // Work directly on rollout items, and cut the vector at the nth user message input. let items: Vec = history.get_rollout_items(); @@ -253,7 +253,7 @@ mod tests { .cloned() .map(RolloutItem::ResponseItem) .collect(); - let truncated = truncate_before_nth_user_message(InitialHistory::Forked(initial), 1); + let truncated = truncate_before_nth_user_message(&InitialHistory::Forked(initial), 1); let got_items = truncated.get_rollout_items(); let expected_items = vec![ RolloutItem::ResponseItem(items[0].clone()), @@ -270,7 +270,7 @@ mod tests { .cloned() .map(RolloutItem::ResponseItem) .collect(); - let truncated2 = truncate_before_nth_user_message(InitialHistory::Forked(initial2), 2); + let truncated2 = truncate_before_nth_user_message(&InitialHistory::Forked(initial2), 2); assert!(matches!(truncated2, InitialHistory::New)); } @@ -289,7 +289,7 @@ mod tests { .map(RolloutItem::ResponseItem) .collect(); - let truncated = truncate_before_nth_user_message(InitialHistory::Forked(rollout_items), 1); + let truncated = truncate_before_nth_user_message(&InitialHistory::Forked(rollout_items), 1); let got_items = truncated.get_rollout_items(); let expected: Vec = vec![ diff --git a/codex-rs/core/src/custom_prompts.rs b/codex-rs/core/src/custom_prompts.rs index 4974e7c52d..18cd1deeb8 100644 --- a/codex-rs/core/src/custom_prompts.rs +++ b/codex-rs/core/src/custom_prompts.rs @@ -52,7 +52,7 @@ pub async fn discover_prompts_in_excluding( let Some(name) = path .file_stem() .and_then(|s| s.to_str()) - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) else { continue; }; diff --git a/codex-rs/core/src/git_info.rs b/codex-rs/core/src/git_info.rs index 832b28f114..45dd557788 100644 --- a/codex-rs/core/src/git_info.rs +++ b/codex-rs/core/src/git_info.rs @@ -200,7 +200,7 @@ async fn get_git_remotes(cwd: &Path) -> Option> { let mut remotes: Vec = String::from_utf8(output.stdout) .ok()? .lines() - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .collect(); if let Some(pos) = remotes.iter().position(|r| r == "origin") { let origin = remotes.remove(pos); @@ -477,7 +477,7 @@ async fn diff_against_sha(cwd: &Path, sha: &GitSha) -> Option { let untracked: Vec = String::from_utf8(untracked_output.stdout) .ok()? .lines() - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .filter(|s| !s.is_empty()) .collect(); diff --git a/codex-rs/core/src/is_safe_command.rs b/codex-rs/core/src/is_safe_command.rs index f54e247fd8..06c85b458a 100644 --- a/codex-rs/core/src/is_safe_command.rs +++ b/codex-rs/core/src/is_safe_command.rs @@ -162,7 +162,7 @@ mod tests { use super::*; fn vec_str(args: &[&str]) -> Vec { - args.iter().map(|s| s.to_string()).collect() + args.iter().map(std::string::ToString::to_string).collect() } #[test] diff --git a/codex-rs/core/src/openai_tools.rs b/codex-rs/core/src/openai_tools.rs index 05b71ce6ea..cc01005475 100644 --- a/codex-rs/core/src/openai_tools.rs +++ b/codex-rs/core/src/openai_tools.rs @@ -403,7 +403,7 @@ fn sanitize_json_schema(value: &mut JsonValue) { let mut ty = map .get("type") .and_then(|v| v.as_str()) - .map(|s| s.to_string()); + .map(std::string::ToString::to_string); // If type is an array (union), pick first supported; else leave to inference if ty.is_none() diff --git a/codex-rs/core/src/parse_command.rs b/codex-rs/core/src/parse_command.rs index 1d2948a406..c1488f3c10 100644 --- a/codex-rs/core/src/parse_command.rs +++ b/codex-rs/core/src/parse_command.rs @@ -40,7 +40,7 @@ impl From for codex_protocol::parse_command::ParsedCommand { } fn shlex_join(tokens: &[String]) -> String { - shlex_try_join(tokens.iter().map(|s| s.as_str())) + shlex_try_join(tokens.iter().map(std::string::String::as_str)) .unwrap_or_else(|_| "".to_string()) } @@ -74,14 +74,18 @@ mod tests { use super::*; fn shlex_split_safe(s: &str) -> Vec { - shlex_split(s).unwrap_or_else(|| s.split_whitespace().map(|s| s.to_string()).collect()) + shlex_split(s).unwrap_or_else(|| { + s.split_whitespace() + .map(std::string::ToString::to_string) + .collect() + }) } fn vec_str(args: &[&str]) -> Vec { - args.iter().map(|s| s.to_string()).collect() + args.iter().map(std::string::ToString::to_string).collect() } - fn assert_parsed(args: &[String], expected: Vec) { + fn assert_parsed(args: &[String], expected: &[ParsedCommand]) { let out = parse_command(args); assert_eq!(out, expected); } @@ -90,7 +94,7 @@ mod tests { fn git_status_is_unknown() { assert_parsed( &vec_str(&["git", "status"]), - vec![ParsedCommand::Unknown { + &[ParsedCommand::Unknown { cmd: "git status".to_string(), }], ); @@ -101,7 +105,7 @@ mod tests { let inner = "git status | wc -l"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Unknown { + &[ParsedCommand::Unknown { cmd: "git status".to_string(), }], ); @@ -112,7 +116,7 @@ mod tests { let inner = "echo foo > bar"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Unknown { + &[ParsedCommand::Unknown { cmd: "echo foo > bar".to_string(), }], ); @@ -124,7 +128,7 @@ mod tests { "rg --version && node -v && pnpm -v && rg --files | wc -l && rg --files | head -n 40"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ + &[ // Expect commands in left-to-right execution order ParsedCommand::Search { cmd: "rg --version".to_string(), @@ -154,7 +158,7 @@ mod tests { let inner = "rg -n \"navigate-to-route\" -S"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg -n navigate-to-route -S".to_string(), query: Some("navigate-to-route".to_string()), path: None, @@ -168,7 +172,7 @@ mod tests { let inner = "rg -n \"BUG|FIXME|TODO|XXX|HACK\" -S | head -n 200"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ + &[ ParsedCommand::Search { cmd: "rg -n 'BUG|FIXME|TODO|XXX|HACK' -S".to_string(), query: Some("BUG|FIXME|TODO|XXX|HACK".to_string()), @@ -186,7 +190,7 @@ mod tests { let inner = "rg --files webview/src | sed -n"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files webview/src".to_string(), query: None, path: Some("webview".to_string()), @@ -199,7 +203,7 @@ mod tests { let inner = "rg --files | head -n 50"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ + &[ ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, @@ -217,7 +221,7 @@ mod tests { let inner = "cat webview/README.md"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: inner.to_string(), name: "README.md".to_string(), }], @@ -228,7 +232,7 @@ mod tests { fn cd_then_cat_is_single_read() { assert_parsed( &shlex_split_safe("cd foo && cat foo.txt"), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "cat foo.txt".to_string(), name: "foo.txt".to_string(), }], @@ -240,7 +244,7 @@ mod tests { // Ensure a leading `cd` inside bash -lc is dropped when followed by another command. assert_parsed( &shlex_split_safe("bash -lc 'cd foo && bar'"), - vec![ParsedCommand::Unknown { + &[ParsedCommand::Unknown { cmd: "bar".to_string(), }], ); @@ -250,7 +254,7 @@ mod tests { fn bash_cd_then_cat_is_read() { assert_parsed( &shlex_split_safe("bash -lc 'cd foo && cat foo.txt'"), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "cat foo.txt".to_string(), name: "foo.txt".to_string(), }], @@ -262,7 +266,7 @@ mod tests { let inner = "ls -la | sed -n '1,120p'"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::ListFiles { + &[ParsedCommand::ListFiles { cmd: "ls -la".to_string(), path: None, }], @@ -274,7 +278,7 @@ mod tests { let inner = "head -n 50 Cargo.toml"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: inner.to_string(), name: "Cargo.toml".to_string(), }], @@ -286,7 +290,7 @@ mod tests { let inner = "cat tui/Cargo.toml | sed -n '1,200p'"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: inner.to_string(), name: "Cargo.toml".to_string(), }], @@ -298,7 +302,7 @@ mod tests { let inner = "tail -n +522 README.md"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: inner.to_string(), name: "README.md".to_string(), }], @@ -322,7 +326,7 @@ mod tests { fn supports_npm_run_build_is_unknown() { assert_parsed( &vec_str(&["npm", "run", "build"]), - vec![ParsedCommand::Unknown { + &[ParsedCommand::Unknown { cmd: "npm run build".to_string(), }], ); @@ -332,7 +336,7 @@ mod tests { fn supports_grep_recursive_current_dir() { assert_parsed( &vec_str(&["grep", "-R", "CODEX_SANDBOX_ENV_VAR", "-n", "."]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "grep -R CODEX_SANDBOX_ENV_VAR -n .".to_string(), query: Some("CODEX_SANDBOX_ENV_VAR".to_string()), path: Some(".".to_string()), @@ -350,7 +354,7 @@ mod tests { "-n", "core/src/spawn.rs", ]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "grep -R CODEX_SANDBOX_ENV_VAR -n core/src/spawn.rs".to_string(), query: Some("CODEX_SANDBOX_ENV_VAR".to_string()), path: Some("spawn.rs".to_string()), @@ -364,7 +368,7 @@ mod tests { // Previously, grep queries were passed through short_display_path, which is incorrect. assert_parsed( &shlex_split_safe("grep -R src/main.rs -n ."), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "grep -R src/main.rs -n .".to_string(), query: Some("src/main.rs".to_string()), path: Some(".".to_string()), @@ -376,7 +380,7 @@ mod tests { fn supports_grep_weird_backtick_in_query() { assert_parsed( &shlex_split_safe("grep -R COD`EX_SANDBOX -n"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "grep -R 'COD`EX_SANDBOX' -n".to_string(), query: Some("COD`EX_SANDBOX".to_string()), path: None, @@ -388,7 +392,7 @@ mod tests { fn supports_cd_and_rg_files() { assert_parsed( &shlex_split_safe("cd codex-rs && rg --files"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -480,7 +484,7 @@ mod tests { let inner = "nl -ba core/src/parse_command.rs | sed -n '1200,1720p'"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: inner.to_string(), name: "parse_command.rs".to_string(), }], @@ -492,7 +496,7 @@ mod tests { let inner = "sed -n '2000,2200p' tui/src/history_cell.rs"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: inner.to_string(), name: "history_cell.rs".to_string(), }], @@ -505,7 +509,7 @@ mod tests { r#"printf "\n===== ansi-escape/Cargo.toml =====\n"; cat -- ansi-escape/Cargo.toml"#; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "cat -- ansi-escape/Cargo.toml".to_string(), name: "Cargo.toml".to_string(), }], @@ -518,7 +522,7 @@ mod tests { let inner = "yes | rg --files"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -534,7 +538,7 @@ mod tests { ); assert_parsed( &args, - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "sed -n '260,640p' exec/src/event_processor_with_human_output.rs".to_string(), name: "event_processor_with_human_output.rs".to_string(), }], @@ -545,7 +549,7 @@ mod tests { fn preserves_rg_with_spaces() { assert_parsed( &shlex_split_safe("yes | rg -n 'foo bar' -S"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg -n 'foo bar' -S".to_string(), query: Some("foo bar".to_string()), path: None, @@ -557,7 +561,7 @@ mod tests { fn ls_with_glob() { assert_parsed( &shlex_split_safe("ls -I '*.test.js'"), - vec![ParsedCommand::ListFiles { + &[ParsedCommand::ListFiles { cmd: "ls -I '*.test.js'".to_string(), path: None, }], @@ -568,7 +572,7 @@ mod tests { fn trim_on_semicolon() { assert_parsed( &shlex_split_safe("rg foo ; echo done"), - vec![ + &[ ParsedCommand::Search { cmd: "rg foo".to_string(), query: Some("foo".to_string()), @@ -586,7 +590,7 @@ mod tests { // Ensure we split commands on the logical OR operator as well. assert_parsed( &shlex_split_safe("rg foo || echo done"), - vec![ + &[ ParsedCommand::Search { cmd: "rg foo".to_string(), query: Some("foo".to_string()), @@ -641,7 +645,7 @@ mod tests { }, ]; - assert_parsed(&args, expected); + assert_parsed(&args, &expected); } #[test] @@ -649,7 +653,7 @@ mod tests { // `true` should be dropped from parsed sequences assert_parsed( &shlex_split_safe("true && rg --files"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -658,7 +662,7 @@ mod tests { assert_parsed( &shlex_split_safe("rg --files && true"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -671,7 +675,7 @@ mod tests { let inner = "true && rg --files"; assert_parsed( &vec_str(&["bash", "-lc", inner]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -681,7 +685,7 @@ mod tests { let inner2 = "rg --files || true"; assert_parsed( &vec_str(&["bash", "-lc", inner2]), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -693,7 +697,7 @@ mod tests { fn shorten_path_on_windows() { assert_parsed( &shlex_split_safe(r#"cat "pkg\src\main.rs""#), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: r#"cat "pkg\\src\\main.rs""#.to_string(), name: "main.rs".to_string(), }], @@ -704,7 +708,7 @@ mod tests { fn head_with_no_space() { assert_parsed( &shlex_split_safe("bash -lc 'head -n50 Cargo.toml'"), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "head -n50 Cargo.toml".to_string(), name: "Cargo.toml".to_string(), }], @@ -717,7 +721,7 @@ mod tests { let inner = "rg --files | head -n 1"; assert_parsed( &shlex_split_safe(inner), - vec![ + &[ ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, @@ -734,7 +738,7 @@ mod tests { fn tail_with_no_space() { assert_parsed( &shlex_split_safe("bash -lc 'tail -n+10 README.md'"), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "tail -n+10 README.md".to_string(), name: "README.md".to_string(), }], @@ -745,7 +749,7 @@ mod tests { fn grep_with_query_and_path() { assert_parsed( &shlex_split_safe("grep -R TODO src"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "grep -R TODO src".to_string(), query: Some("TODO".to_string()), path: Some("src".to_string()), @@ -757,7 +761,7 @@ mod tests { fn rg_with_equals_style_flags() { assert_parsed( &shlex_split_safe("rg --colors=never -n foo src"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg '--colors=never' -n foo src".to_string(), query: Some("foo".to_string()), path: Some("src".to_string()), @@ -770,7 +774,7 @@ mod tests { // cat -- should be treated as a read of that file assert_parsed( &shlex_split_safe("cat -- ./-strange-file-name"), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "cat -- ./-strange-file-name".to_string(), name: "-strange-file-name".to_string(), }], @@ -779,7 +783,7 @@ mod tests { // sed -n should be treated as a read of assert_parsed( &shlex_split_safe("sed -n '12,20p' Cargo.toml"), - vec![ParsedCommand::Read { + &[ParsedCommand::Read { cmd: "sed -n '12,20p' Cargo.toml".to_string(), name: "Cargo.toml".to_string(), }], @@ -791,7 +795,7 @@ mod tests { // When an `nl` stage has only flags, it should be dropped from the summary assert_parsed( &shlex_split_safe("rg --files | nl -ba"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "rg --files".to_string(), query: None, path: None, @@ -803,7 +807,7 @@ mod tests { fn ls_with_time_style_and_path() { assert_parsed( &shlex_split_safe("ls --time-style=long-iso ./dist"), - vec![ParsedCommand::ListFiles { + &[ParsedCommand::ListFiles { cmd: "ls '--time-style=long-iso' ./dist".to_string(), // short_display_path drops "dist" and shows "." as the last useful segment path: Some(".".to_string()), @@ -815,7 +819,7 @@ mod tests { fn fd_file_finder_variants() { assert_parsed( &shlex_split_safe("fd -t f src/"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "fd -t f src/".to_string(), query: None, path: Some("src".to_string()), @@ -825,7 +829,7 @@ mod tests { // fd with query and path should capture both assert_parsed( &shlex_split_safe("fd main src"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "fd main src".to_string(), query: Some("main".to_string()), path: Some("src".to_string()), @@ -837,7 +841,7 @@ mod tests { fn find_basic_name_filter() { assert_parsed( &shlex_split_safe("find . -name '*.rs'"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "find . -name '*.rs'".to_string(), query: Some("*.rs".to_string()), path: Some(".".to_string()), @@ -849,7 +853,7 @@ mod tests { fn find_type_only_path() { assert_parsed( &shlex_split_safe("find src -type f"), - vec![ParsedCommand::Search { + &[ParsedCommand::Search { cmd: "find src -type f".to_string(), query: None, path: Some("src".to_string()), @@ -894,16 +898,16 @@ fn simplify_once(commands: &[ParsedCommand]) -> Option> { // echo ... && ...rest => ...rest if let ParsedCommand::Unknown { cmd } = &commands[0] - && shlex_split(cmd).is_some_and(|t| t.first().map(|s| s.as_str()) == Some("echo")) + && shlex_split(cmd) + .is_some_and(|t| t.first().map(std::string::String::as_str) == Some("echo")) { return Some(commands[1..].to_vec()); } // cd foo && [any command] => [any command] (keep non-cd when a cd is followed by something) if let Some(idx) = commands.iter().position(|pc| match pc { - ParsedCommand::Unknown { cmd } => { - shlex_split(cmd).is_some_and(|t| t.first().map(|s| s.as_str()) == Some("cd")) - } + ParsedCommand::Unknown { cmd } => shlex_split(cmd) + .is_some_and(|t| t.first().map(std::string::String::as_str) == Some("cd")), _ => false, }) && commands.len() > idx + 1 { @@ -1035,7 +1039,7 @@ fn short_display_path(path: &str) -> String { }); parts .next() - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .unwrap_or_else(|| trimmed.to_string()) } @@ -1190,8 +1194,8 @@ fn parse_bash_lc_commands(original: &[String]) -> Option> { if had_connectors { let has_pipe = script_tokens.iter().any(|t| t == "|"); let has_sed_n = script_tokens.windows(2).any(|w| { - w.first().map(|s| s.as_str()) == Some("sed") - && w.get(1).map(|s| s.as_str()) == Some("-n") + w.first().map(std::string::String::as_str) == Some("sed") + && w.get(1).map(std::string::String::as_str) == Some("-n") }); if has_pipe && has_sed_n { ParsedCommand::Read { @@ -1271,7 +1275,8 @@ fn is_small_formatting_command(tokens: &[String]) -> bool { // Keep `sed -n file` (treated as a file read elsewhere); // otherwise consider it a formatting helper in a pipeline. tokens.len() < 4 - || !(tokens[1] == "-n" && is_valid_sed_n_arg(tokens.get(2).map(|s| s.as_str()))) + || !(tokens[1] == "-n" + && is_valid_sed_n_arg(tokens.get(2).map(std::string::String::as_str))) } _ => false, } @@ -1318,7 +1323,10 @@ fn summarize_main_tokens(main_cmd: &[String]) -> ParsedCommand { (None, non_flags.first().map(|s| short_display_path(s))) } else { ( - non_flags.first().cloned().map(|s| s.to_string()), + non_flags + .first() + .cloned() + .map(std::string::ToString::to_string), non_flags.get(1).map(|s| short_display_path(s)), ) }; @@ -1353,7 +1361,10 @@ fn summarize_main_tokens(main_cmd: &[String]) -> ParsedCommand { .collect(); // Do not shorten the query: grep patterns may legitimately contain slashes // and should be preserved verbatim. Only paths should be shortened. - let query = non_flags.first().cloned().map(|s| s.to_string()); + let query = non_flags + .first() + .cloned() + .map(std::string::ToString::to_string); let path = non_flags.get(1).map(|s| short_display_path(s)); ParsedCommand::Search { cmd: shlex_join(main_cmd), @@ -1363,11 +1374,12 @@ fn summarize_main_tokens(main_cmd: &[String]) -> ParsedCommand { } Some((head, tail)) if head == "cat" => { // Support both `cat ` and `cat -- ` forms. - let effective_tail: &[String] = if tail.first().map(|s| s.as_str()) == Some("--") { - &tail[1..] - } else { - tail - }; + let effective_tail: &[String] = + if tail.first().map(std::string::String::as_str) == Some("--") { + &tail[1..] + } else { + tail + }; if effective_tail.len() == 1 { let name = short_display_path(&effective_tail[0]); ParsedCommand::Read { @@ -1479,7 +1491,7 @@ fn summarize_main_tokens(main_cmd: &[String]) -> ParsedCommand { if head == "sed" && tail.len() >= 3 && tail[0] == "-n" - && is_valid_sed_n_arg(tail.get(1).map(|s| s.as_str())) => + && is_valid_sed_n_arg(tail.get(1).map(std::string::String::as_str)) => { if let Some(path) = tail.get(2) { let name = short_display_path(path); diff --git a/codex-rs/core/src/plan_tool.rs b/codex-rs/core/src/plan_tool.rs index a712b8b8dd..3bd7fc6dc5 100644 --- a/codex-rs/core/src/plan_tool.rs +++ b/codex-rs/core/src/plan_tool.rs @@ -65,7 +65,7 @@ At most one step can be in_progress at a time. /// than forcing it to come up and document a plan (TBD how that affects performance). pub(crate) async fn handle_update_plan( session: &Session, - arguments: String, + arguments: &str, sub_id: String, call_id: String, ) -> ResponseInputItem { @@ -91,10 +91,10 @@ pub(crate) async fn handle_update_plan( } fn parse_update_plan_arguments( - arguments: String, + arguments: &str, call_id: &str, ) -> Result> { - match serde_json::from_str::(&arguments) { + match serde_json::from_str::(arguments) { Ok(args) => Ok(args), Err(e) => { let output = ResponseInputItem::FunctionCallOutput { diff --git a/codex-rs/core/src/rollout/list.rs b/codex-rs/core/src/rollout/list.rs index 6f06709396..c89dccdca7 100644 --- a/codex-rs/core/src/rollout/list.rs +++ b/codex-rs/core/src/rollout/list.rs @@ -372,7 +372,7 @@ pub async fn find_conversation_path_by_id_str( &root, exclude, threads, - cancel, + &cancel, compute_indices, ) .map_err(|e| io::Error::other(format!("file search failed: {e}")))?; diff --git a/codex-rs/core/src/shell.rs b/codex-rs/core/src/shell.rs index 3bfef7e1e0..1f06f1abe1 100644 --- a/codex-rs/core/src/shell.rs +++ b/codex-rs/core/src/shell.rs @@ -73,7 +73,8 @@ impl Shell { return Some(command); } - let joined = shlex::try_join(command.iter().map(|s| s.as_str())).ok(); + let joined = + shlex::try_join(command.iter().map(std::string::String::as_str)).ok(); return joined.map(|arg| { vec![ ps.exe.clone(), @@ -111,7 +112,7 @@ fn format_shell_invocation_with_rc( rc_path: &str, ) -> Option> { let joined = strip_bash_lc(command) - .or_else(|| shlex::try_join(command.iter().map(|s| s.as_str())).ok())?; + .or_else(|| shlex::try_join(command.iter().map(std::string::String::as_str)).ok())?; let rc_command = if std::path::Path::new(rc_path).exists() { format!("source {rc_path} && ({joined})") @@ -326,8 +327,9 @@ mod tests { bashrc_path: bashrc_path.to_str().unwrap().to_string(), }); - let actual_cmd = shell - .format_default_shell_invocation(input.iter().map(|s| s.to_string()).collect()); + let actual_cmd = shell.format_default_shell_invocation( + input.iter().map(std::string::ToString::to_string).collect(), + ); let expected_cmd = expected_cmd .iter() .map(|s| s.replace("BASHRC_PATH", bashrc_path.to_str().unwrap())) @@ -433,8 +435,9 @@ mod macos_tests { zshrc_path: zshrc_path.to_str().unwrap().to_string(), }); - let actual_cmd = shell - .format_default_shell_invocation(input.iter().map(|s| s.to_string()).collect()); + let actual_cmd = shell.format_default_shell_invocation( + input.iter().map(std::string::ToString::to_string).collect(), + ); let expected_cmd = expected_cmd .iter() .map(|s| s.replace("ZSHRC_PATH", zshrc_path.to_str().unwrap())) diff --git a/codex-rs/core/src/terminal.rs b/codex-rs/core/src/terminal.rs index 02104f8be5..d021cbe0e2 100644 --- a/codex-rs/core/src/terminal.rs +++ b/codex-rs/core/src/terminal.rs @@ -17,56 +17,56 @@ fn is_valid_header_value_char(c: char) -> bool { c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' || c == '/' } -fn sanitize_header_value(value: String) -> String { +fn sanitize_header_value(value: &str) -> String { value.replace(|c| !is_valid_header_value_char(c), "_") } fn detect_terminal() -> String { - sanitize_header_value( - if let Ok(tp) = std::env::var("TERM_PROGRAM") - && !tp.trim().is_empty() - { - let ver = std::env::var("TERM_PROGRAM_VERSION").ok(); - match ver { - Some(v) if !v.trim().is_empty() => format!("{tp}/{v}"), - _ => tp, - } - } else if let Ok(v) = std::env::var("WEZTERM_VERSION") { - if !v.trim().is_empty() { - format!("WezTerm/{v}") - } else { - "WezTerm".to_string() - } - } else if std::env::var("KITTY_WINDOW_ID").is_ok() - || std::env::var("TERM") - .map(|t| t.contains("kitty")) - .unwrap_or(false) - { - "kitty".to_string() - } else if std::env::var("ALACRITTY_SOCKET").is_ok() - || std::env::var("TERM") - .map(|t| t == "alacritty") - .unwrap_or(false) - { - "Alacritty".to_string() - } else if let Ok(v) = std::env::var("KONSOLE_VERSION") { - if !v.trim().is_empty() { - format!("Konsole/{v}") - } else { - "Konsole".to_string() - } - } else if std::env::var("GNOME_TERMINAL_SCREEN").is_ok() { - return "gnome-terminal".to_string(); - } else if let Ok(v) = std::env::var("VTE_VERSION") { - if !v.trim().is_empty() { - format!("VTE/{v}") - } else { - "VTE".to_string() - } - } else if std::env::var("WT_SESSION").is_ok() { - return "WindowsTerminal".to_string(); + let raw = if let Ok(tp) = std::env::var("TERM_PROGRAM") + && !tp.trim().is_empty() + { + let ver = std::env::var("TERM_PROGRAM_VERSION").ok(); + match ver { + Some(v) if !v.trim().is_empty() => format!("{tp}/{v}"), + _ => tp, + } + } else if let Ok(v) = std::env::var("WEZTERM_VERSION") { + if !v.trim().is_empty() { + format!("WezTerm/{v}") } else { - std::env::var("TERM").unwrap_or_else(|_| "unknown".to_string()) - }, - ) + "WezTerm".to_string() + } + } else if std::env::var("KITTY_WINDOW_ID").is_ok() + || std::env::var("TERM") + .map(|t| t.contains("kitty")) + .unwrap_or(false) + { + "kitty".to_string() + } else if std::env::var("ALACRITTY_SOCKET").is_ok() + || std::env::var("TERM") + .map(|t| t == "alacritty") + .unwrap_or(false) + { + "Alacritty".to_string() + } else if let Ok(v) = std::env::var("KONSOLE_VERSION") { + if !v.trim().is_empty() { + format!("Konsole/{v}") + } else { + "Konsole".to_string() + } + } else if std::env::var("GNOME_TERMINAL_SCREEN").is_ok() { + return sanitize_header_value("gnome-terminal"); + } else if let Ok(v) = std::env::var("VTE_VERSION") { + if !v.trim().is_empty() { + format!("VTE/{v}") + } else { + "VTE".to_string() + } + } else if std::env::var("WT_SESSION").is_ok() { + return sanitize_header_value("WindowsTerminal"); + } else { + std::env::var("TERM").unwrap_or_else(|_| "unknown".to_string()) + }; + + sanitize_header_value(&raw) } diff --git a/codex-rs/core/src/turn_diff_tracker.rs b/codex-rs/core/src/turn_diff_tracker.rs index 6c12d6cd46..06c40deb90 100644 --- a/codex-rs/core/src/turn_diff_tracker.rs +++ b/codex-rs/core/src/turn_diff_tracker.rs @@ -65,7 +65,7 @@ impl TurnDiffTracker { let baseline_file_info = if path.exists() { let mode = file_mode_for_path(path); let mode_val = mode.unwrap_or(FileMode::Regular); - let content = blob_bytes(path, &mode_val).unwrap_or_default(); + let content = blob_bytes(path, mode_val).unwrap_or_default(); let oid = if mode == Some(FileMode::Symlink) { format!("{:x}", git_blob_sha1_hex_bytes(&content)) } else { @@ -266,7 +266,7 @@ impl TurnDiffTracker { }; let current_mode = file_mode_for_path(¤t_external_path).unwrap_or(FileMode::Regular); - let right_bytes = blob_bytes(¤t_external_path, ¤t_mode); + let right_bytes = blob_bytes(¤t_external_path, current_mode); // Compute displays with &mut self before borrowing any baseline content. let left_display = self.relative_to_git_root_str(&baseline_external_path); @@ -388,7 +388,7 @@ enum FileMode { } impl FileMode { - fn as_str(&self) -> &'static str { + fn as_str(self) -> &'static str { match self { FileMode::Regular => "100644", #[cfg(unix)] @@ -427,9 +427,9 @@ fn file_mode_for_path(_path: &Path) -> Option { Some(FileMode::Regular) } -fn blob_bytes(path: &Path, mode: &FileMode) -> Option> { +fn blob_bytes(path: &Path, mode: FileMode) -> Option> { if path.exists() { - let contents = if *mode == FileMode::Symlink { + let contents = if mode == FileMode::Symlink { symlink_blob_bytes(path) .ok_or_else(|| anyhow!("failed to read symlink target for {}", path.display())) } else { diff --git a/codex-rs/core/tests/suite/exec.rs b/codex-rs/core/tests/suite/exec.rs index 280917b530..20aafc4b62 100644 --- a/codex-rs/core/tests/suite/exec.rs +++ b/codex-rs/core/tests/suite/exec.rs @@ -29,7 +29,7 @@ async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result) -> Vec { +fn collect_stdout_events(rx: &Receiver) -> Vec { let mut out = Vec::new(); while let Ok(ev) = rx.try_recv() { if let EventMsg::ExecCommandOutputDelta(ExecCommandOutputDeltaEvent { @@ -79,7 +79,7 @@ async fn test_exec_stdout_stream_events_echo() { assert_eq!(result.exit_code, 0); assert_eq!(result.stdout.text, "hello-world\n"); - let streamed = collect_stdout_events(rx); + let streamed = collect_stdout_events(&rx); // We should have received at least the same contents (possibly in one chunk) assert_eq!(String::from_utf8_lossy(&streamed), "hello-world\n"); } diff --git a/codex-rs/core/tests/suite/prompt_caching.rs b/codex-rs/core/tests/suite/prompt_caching.rs index a69f57a201..df794bfe7f 100644 --- a/codex-rs/core/tests/suite/prompt_caching.rs +++ b/codex-rs/core/tests/suite/prompt_caching.rs @@ -24,7 +24,7 @@ use wiremock::ResponseTemplate; use wiremock::matchers::method; use wiremock::matchers::path; -fn text_user_input(text: String) -> serde_json::Value { +fn text_user_input(text: &str) -> serde_json::Value { serde_json::json!({ "type": "message", "role": "user", @@ -685,13 +685,13 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() { let shell = default_user_shell().await; let expected_ui_text = "\n\nbe consistent and helpful\n\n"; - let expected_ui_msg = text_user_input(expected_ui_text.to_string()); + let expected_ui_msg = text_user_input(expected_ui_text); - let expected_env_msg_1 = text_user_input(default_env_context_str( + let expected_env_msg_1 = text_user_input(&default_env_context_str( &cwd.path().to_string_lossy(), &shell, )); - let expected_user_message_1 = text_user_input("hello 1".to_string()); + let expected_user_message_1 = text_user_input("hello 1"); let expected_input_1 = serde_json::Value::Array(vec![ expected_ui_msg.clone(), @@ -700,7 +700,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() { ]); assert_eq!(body1["input"], expected_input_1); - let expected_user_message_2 = text_user_input("hello 2".to_string()); + let expected_user_message_2 = text_user_input("hello 2"); let expected_input_2 = serde_json::Value::Array(vec![ expected_ui_msg, expected_env_msg_1, @@ -802,8 +802,8 @@ async fn send_user_turn_with_changes_sends_environment_context() { "content": [ { "type": "input_text", "text": expected_ui_text } ] }); let expected_env_text_1 = default_env_context_str(&default_cwd.to_string_lossy(), &shell); - let expected_env_msg_1 = text_user_input(expected_env_text_1); - let expected_user_message_1 = text_user_input("hello 1".to_string()); + let expected_env_msg_1 = text_user_input(&expected_env_text_1); + let expected_user_message_1 = text_user_input("hello 1"); let expected_input_1 = serde_json::Value::Array(vec![ expected_ui_msg.clone(), expected_env_msg_1.clone(), @@ -811,7 +811,7 @@ async fn send_user_turn_with_changes_sends_environment_context() { ]); assert_eq!(body1["input"], expected_input_1); - let expected_env_msg_2 = text_user_input(format!( + let expected_env_msg_2 = text_user_input(&format!( r#" {} never @@ -820,7 +820,7 @@ async fn send_user_turn_with_changes_sends_environment_context() { "#, default_cwd.to_string_lossy() )); - let expected_user_message_2 = text_user_input("hello 2".to_string()); + let expected_user_message_2 = text_user_input("hello 2"); let expected_input_2 = serde_json::Value::Array(vec![ expected_ui_msg, expected_env_msg_1, diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index 5ef7d51a26..80967398c0 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -573,7 +573,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { } fn escape_command(command: &[String]) -> String { - try_join(command.iter().map(|s| s.as_str())).unwrap_or_else(|_| command.join(" ")) + try_join(command.iter().map(std::string::String::as_str)).unwrap_or_else(|_| command.join(" ")) } fn format_file_change(change: &FileChange) -> &'static str { diff --git a/codex-rs/exec/tests/suite/resume.rs b/codex-rs/exec/tests/suite/resume.rs index 5868ed8eb2..56f74991c5 100644 --- a/codex-rs/exec/tests/suite/resume.rs +++ b/codex-rs/exec/tests/suite/resume.rs @@ -45,7 +45,7 @@ fn find_session_file_containing_marker( && payload.get("type").and_then(|t| t.as_str()) == Some("message") && payload .get("content") - .map(|c| c.to_string()) + .map(std::string::ToString::to_string) .unwrap_or_default() .contains(marker) { diff --git a/codex-rs/execpolicy/src/arg_resolver.rs b/codex-rs/execpolicy/src/arg_resolver.rs index 4342f1b45b..1a5f585acf 100644 --- a/codex-rs/execpolicy/src/arg_resolver.rs +++ b/codex-rs/execpolicy/src/arg_resolver.rs @@ -89,15 +89,14 @@ pub fn resolve_observed_args_with_patterns( program: program.to_string(), matcher: pattern, }); - } else { - for positional_arg in vararg { - let matched_arg = MatchedArg::new( - positional_arg.index, - pattern.arg_type(), - &positional_arg.value.clone(), - )?; - matched_args.push(matched_arg); - } + } + for positional_arg in vararg { + let matched_arg = MatchedArg::new( + positional_arg.index, + pattern.arg_type(), + &positional_arg.value.clone(), + )?; + matched_args.push(matched_arg); } } ArgMatcherCardinality::ZeroOrMore => { diff --git a/codex-rs/execpolicy/src/execv_checker.rs b/codex-rs/execpolicy/src/execv_checker.rs index ae61d54cf8..7d45fe39c8 100644 --- a/codex-rs/execpolicy/src/execv_checker.rs +++ b/codex-rs/execpolicy/src/execv_checker.rs @@ -108,7 +108,7 @@ fn ensure_absolute_path(path: &str, cwd: &Option) -> Result { file.absolutize() }; result - .map(|path| path.into_owned()) + .map(std::borrow::Cow::into_owned) .map_err(|error| CannotCanonicalizePath { file: path.to_string(), error: error.kind(), diff --git a/codex-rs/execpolicy/src/main.rs b/codex-rs/execpolicy/src/main.rs index 0a4b1764ca..68a72c04ff 100644 --- a/codex-rs/execpolicy/src/main.rs +++ b/codex-rs/execpolicy/src/main.rs @@ -10,6 +10,7 @@ use codex_execpolicy::get_default_policy; use serde::Deserialize; use serde::Serialize; use serde::de; +use starlark::Error as StarlarkError; use std::path::PathBuf; use std::str::FromStr; @@ -71,13 +72,13 @@ fn main() -> Result<()> { } None => get_default_policy(), }; - let policy = policy.map_err(|err| err.into_anyhow())?; + let policy = policy.map_err(StarlarkError::into_anyhow)?; let exec = match args.command { Command::Check { command } => match command.split_first() { Some((first, rest)) => ExecArg { program: first.to_string(), - args: rest.iter().map(|s| s.to_string()).collect(), + args: rest.to_vec(), }, None => { eprintln!("no command provided"); @@ -161,6 +162,6 @@ impl FromStr for ExecArg { type Err = anyhow::Error; fn from_str(s: &str) -> Result { - serde_json::from_str(s).map_err(|e| e.into()) + serde_json::from_str(s).map_err(Into::into) } } diff --git a/codex-rs/execpolicy/src/policy.rs b/codex-rs/execpolicy/src/policy.rs index 825d6164a5..f136bec2c4 100644 --- a/codex-rs/execpolicy/src/policy.rs +++ b/codex-rs/execpolicy/src/policy.rs @@ -22,7 +22,7 @@ impl Policy { pub fn new( programs: MultiMap, forbidden_program_regexes: Vec, - forbidden_substrings: Vec, + forbidden_substrings: &[String], ) -> std::result::Result { let forbidden_substrings_pattern = if forbidden_substrings.is_empty() { None diff --git a/codex-rs/execpolicy/src/policy_parser.rs b/codex-rs/execpolicy/src/policy_parser.rs index b065c109a8..5e99bd86e0 100644 --- a/codex-rs/execpolicy/src/policy_parser.rs +++ b/codex-rs/execpolicy/src/policy_parser.rs @@ -97,7 +97,7 @@ impl PolicyBuilder { let programs = self.programs.into_inner(); let forbidden_program_regexes = self.forbidden_program_regexes.into_inner(); let forbidden_substrings = self.forbidden_substrings.into_inner(); - Policy::new(programs, forbidden_program_regexes, forbidden_substrings) + Policy::new(programs, forbidden_program_regexes, &forbidden_substrings) } fn add_program_spec(&self, program_spec: ProgramSpec) { diff --git a/codex-rs/execpolicy/src/program.rs b/codex-rs/execpolicy/src/program.rs index fbe0a104af..68236b2c1f 100644 --- a/codex-rs/execpolicy/src/program.rs +++ b/codex-rs/execpolicy/src/program.rs @@ -169,7 +169,7 @@ impl ProgramSpec { let mut options = self .required_options .difference(&matched_opt_names) - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .collect::>(); options.sort(); return Err(Error::MissingRequiredOptions { diff --git a/codex-rs/file-search/src/lib.rs b/codex-rs/file-search/src/lib.rs index 3238c99f24..468406a92f 100644 --- a/codex-rs/file-search/src/lib.rs +++ b/codex-rs/file-search/src/lib.rs @@ -103,7 +103,7 @@ pub async fn run_main( &search_directory, exclude, threads, - cancel_flag, + &cancel_flag, compute_indices, )?; let match_count = matches.len(); @@ -127,7 +127,7 @@ pub fn run( search_directory: &Path, exclude: Vec, threads: NonZero, - cancel_flag: Arc, + cancel_flag: &Arc, compute_indices: bool, ) -> anyhow::Result { let pattern = create_pattern(pattern_text); @@ -183,7 +183,7 @@ pub fn run( const CHECK_INTERVAL: usize = 1024; let mut processed = 0; - let cancel = cancel_flag.clone(); + let cancel = Arc::clone(cancel_flag); Box::new(move |entry| { if let Some(path) = get_file_path(&entry, search_directory) { diff --git a/codex-rs/login/src/server.rs b/codex-rs/login/src/server.rs index 26f341b9d9..9ffa2b85e8 100644 --- a/codex-rs/login/src/server.rs +++ b/codex-rs/login/src/server.rs @@ -496,11 +496,11 @@ fn compose_success_url(port: u16, issuer: &str, id_token: &str, access_token: &s .unwrap_or(""); let completed_onboarding = token_claims .get("completed_platform_onboarding") - .and_then(|v| v.as_bool()) + .and_then(serde_json::Value::as_bool) .unwrap_or(false); let is_org_owner = token_claims .get("is_org_owner") - .and_then(|v| v.as_bool()) + .and_then(serde_json::Value::as_bool) .unwrap_or(false); let needs_setup = (!completed_onboarding) && is_org_owner; let plan_type = access_claims diff --git a/codex-rs/mcp-server/src/exec_approval.rs b/codex-rs/mcp-server/src/exec_approval.rs index 1985cc79e6..3903eee7e4 100644 --- a/codex-rs/mcp-server/src/exec_approval.rs +++ b/codex-rs/mcp-server/src/exec_approval.rs @@ -57,8 +57,8 @@ pub(crate) async fn handle_exec_approval_request( event_id: String, call_id: String, ) { - let escaped_command = - shlex::try_join(command.iter().map(|s| s.as_str())).unwrap_or_else(|_| command.join(" ")); + let escaped_command = shlex::try_join(command.iter().map(std::string::String::as_str)) + .unwrap_or_else(|_| command.join(" ")); let message = format!( "Allow Codex to run `{escaped_command}` in `{cwd}`?", cwd = cwd.to_string_lossy() diff --git a/codex-rs/mcp-server/src/lib.rs b/codex-rs/mcp-server/src/lib.rs index b1397846ca..159d3889ff 100644 --- a/codex-rs/mcp-server/src/lib.rs +++ b/codex-rs/mcp-server/src/lib.rs @@ -111,7 +111,7 @@ pub async fn run_main( JSONRPCMessage::Request(r) => processor.process_request(r).await, JSONRPCMessage::Response(r) => processor.process_response(r).await, JSONRPCMessage::Notification(n) => processor.process_notification(n).await, - JSONRPCMessage::Error(e) => processor.process_error(e), + JSONRPCMessage::Error(e) => processor.process_error(&e), } } diff --git a/codex-rs/mcp-server/src/message_processor.rs b/codex-rs/mcp-server/src/message_processor.rs index 5868d60fc0..7ced9c54dd 100644 --- a/codex-rs/mcp-server/src/message_processor.rs +++ b/codex-rs/mcp-server/src/message_processor.rs @@ -106,25 +106,25 @@ impl MessageProcessor { self.handle_ping(request_id, params).await; } McpClientRequest::ListResourcesRequest(params) => { - self.handle_list_resources(params); + self.handle_list_resources(¶ms); } McpClientRequest::ListResourceTemplatesRequest(params) => { - self.handle_list_resource_templates(params); + self.handle_list_resource_templates(¶ms); } McpClientRequest::ReadResourceRequest(params) => { - self.handle_read_resource(params); + self.handle_read_resource(¶ms); } McpClientRequest::SubscribeRequest(params) => { - self.handle_subscribe(params); + self.handle_subscribe(¶ms); } McpClientRequest::UnsubscribeRequest(params) => { - self.handle_unsubscribe(params); + self.handle_unsubscribe(¶ms); } McpClientRequest::ListPromptsRequest(params) => { - self.handle_list_prompts(params); + self.handle_list_prompts(¶ms); } McpClientRequest::GetPromptRequest(params) => { - self.handle_get_prompt(params); + self.handle_get_prompt(¶ms); } McpClientRequest::ListToolsRequest(params) => { self.handle_list_tools(request_id, params).await; @@ -133,10 +133,10 @@ impl MessageProcessor { self.handle_call_tool(request_id, params).await; } McpClientRequest::SetLevelRequest(params) => { - self.handle_set_level(params); + self.handle_set_level(¶ms); } McpClientRequest::CompleteRequest(params) => { - self.handle_complete(params); + self.handle_complete(¶ms); } } } @@ -165,28 +165,28 @@ impl MessageProcessor { self.handle_cancelled_notification(params).await; } ServerNotification::ProgressNotification(params) => { - self.handle_progress_notification(params); + self.handle_progress_notification(¶ms); } ServerNotification::ResourceListChangedNotification(params) => { - self.handle_resource_list_changed(params); + self.handle_resource_list_changed(¶ms); } ServerNotification::ResourceUpdatedNotification(params) => { - self.handle_resource_updated(params); + self.handle_resource_updated(¶ms); } ServerNotification::PromptListChangedNotification(params) => { - self.handle_prompt_list_changed(params); + self.handle_prompt_list_changed(¶ms); } ServerNotification::ToolListChangedNotification(params) => { - self.handle_tool_list_changed(params); + self.handle_tool_list_changed(¶ms); } ServerNotification::LoggingMessageNotification(params) => { - self.handle_logging_message(params); + self.handle_logging_message(¶ms); } } } /// Handle an error object received from the peer. - pub(crate) fn process_error(&mut self, err: JSONRPCError) { + pub(crate) fn process_error(&mut self, err: &JSONRPCError) { tracing::error!("<- error: {:?}", err); } @@ -264,50 +264,49 @@ impl MessageProcessor { fn handle_list_resources( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("resources/list -> params: {:?}", params); } fn handle_list_resource_templates( &self, - params: - ::Params, + params: &::Params, ) { tracing::info!("resources/templates/list -> params: {:?}", params); } fn handle_read_resource( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("resources/read -> params: {:?}", params); } fn handle_subscribe( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("resources/subscribe -> params: {:?}", params); } fn handle_unsubscribe( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("resources/unsubscribe -> params: {:?}", params); } fn handle_list_prompts( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("prompts/list -> params: {:?}", params); } fn handle_get_prompt( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("prompts/get -> params: {:?}", params); } @@ -550,14 +549,14 @@ impl MessageProcessor { fn handle_set_level( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("logging/setLevel -> params: {:?}", params); } fn handle_complete( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("completion/complete -> params: {:?}", params); } @@ -623,14 +622,14 @@ impl MessageProcessor { fn handle_progress_notification( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("notifications/progress -> params: {:?}", params); } fn handle_resource_list_changed( &self, - params: ::Params, + params: &::Params, ) { tracing::info!( "notifications/resources/list_changed -> params: {:?}", @@ -640,28 +639,28 @@ impl MessageProcessor { fn handle_resource_updated( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("notifications/resources/updated -> params: {:?}", params); } fn handle_prompt_list_changed( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("notifications/prompts/list_changed -> params: {:?}", params); } fn handle_tool_list_changed( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("notifications/tools/list_changed -> params: {:?}", params); } fn handle_logging_message( &self, - params: ::Params, + params: &::Params, ) { tracing::info!("notifications/message -> params: {:?}", params); } diff --git a/codex-rs/mcp-server/tests/suite/codex_tool.rs b/codex-rs/mcp-server/tests/suite/codex_tool.rs index e7097b6b34..a0a14490af 100644 --- a/codex-rs/mcp-server/tests/suite/codex_tool.rs +++ b/codex-rs/mcp-server/tests/suite/codex_tool.rs @@ -172,7 +172,7 @@ fn create_expected_elicitation_request( ) -> anyhow::Result { let expected_message = format!( "Allow Codex to run `{}` in `{}`?", - shlex::try_join(command.iter().map(|s| s.as_ref()))?, + shlex::try_join(command.iter().map(std::convert::AsRef::as_ref))?, workdir.to_string_lossy() ); Ok(JSONRPCRequest { diff --git a/codex-rs/mcp-server/tests/suite/interrupt.rs b/codex-rs/mcp-server/tests/suite/interrupt.rs index 113a8dd291..423b12c63e 100644 --- a/codex-rs/mcp-server/tests/suite/interrupt.rs +++ b/codex-rs/mcp-server/tests/suite/interrupt.rs @@ -68,7 +68,7 @@ async fn shell_command_interruption() -> anyhow::Result<()> { "call_sleep", )?]) .await; - create_config_toml(&codex_home, server.uri())?; + create_config_toml(&codex_home, &server.uri())?; // Start MCP server and initialize. let mut mcp = McpProcess::new(&codex_home).await?; @@ -140,7 +140,7 @@ async fn shell_command_interruption() -> anyhow::Result<()> { // Helpers // --------------------------------------------------------------------------- -fn create_config_toml(codex_home: &Path, server_uri: String) -> std::io::Result<()> { +fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<()> { let config_toml = codex_home.join("config.toml"); std::fs::write( config_toml, diff --git a/codex-rs/ollama/src/client.rs b/codex-rs/ollama/src/client.rs index 45e0486156..86f3e153f4 100644 --- a/codex-rs/ollama/src/client.rs +++ b/codex-rs/ollama/src/client.rs @@ -117,7 +117,7 @@ impl OllamaClient { .map(|arr| { arr.iter() .filter_map(|v| v.get("name").and_then(|n| n.as_str())) - .map(|s| s.to_string()) + .map(std::string::ToString::to_string) .collect::>() }) .unwrap_or_default(); diff --git a/codex-rs/ollama/src/parser.rs b/codex-rs/ollama/src/parser.rs index b3ed2ca8c3..ecaf2ad557 100644 --- a/codex-rs/ollama/src/parser.rs +++ b/codex-rs/ollama/src/parser.rs @@ -16,8 +16,8 @@ pub(crate) fn pull_events_from_value(value: &JsonValue) -> Vec { .and_then(|d| d.as_str()) .unwrap_or("") .to_string(); - let total = value.get("total").and_then(|t| t.as_u64()); - let completed = value.get("completed").and_then(|t| t.as_u64()); + let total = value.get("total").and_then(serde_json::Value::as_u64); + let completed = value.get("completed").and_then(serde_json::Value::as_u64); if total.is_some() || completed.is_some() { events.push(PullEvent::ChunkProgress { digest, diff --git a/codex-rs/protocol-ts/src/lib.rs b/codex-rs/protocol-ts/src/lib.rs index 967d9b1fcc..eee62e73d6 100644 --- a/codex-rs/protocol-ts/src/lib.rs +++ b/codex-rs/protocol-ts/src/lib.rs @@ -133,8 +133,9 @@ fn generate_index_ts(out_dir: &Path) -> Result { entries.push(format!("export type {{ {name} }} from \"./{name}\";\n")); } - let mut content = - String::with_capacity(HEADER.len() + entries.iter().map(|s| s.len()).sum::()); + let mut content = String::with_capacity( + HEADER.len() + entries.iter().map(std::string::String::len).sum::(), + ); content.push_str(HEADER); for line in &entries { content.push_str(line); diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 138d65a052..c8a4339356 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -188,10 +188,8 @@ impl App { } TuiEvent::Draw => { self.chat_widget.maybe_post_pending_notification(tui); - if self - .chat_widget - .handle_paste_burst_tick(tui.frame_requester()) - { + let frame_requester = tui.frame_requester(); + if self.chat_widget.handle_paste_burst_tick(&frame_requester) { return Ok(true); } tui.draw( @@ -300,11 +298,11 @@ impl App { } AppEvent::StartFileSearch(query) => { if !query.is_empty() { - self.file_search.on_user_query(query); + self.file_search.on_user_query(&query); } } AppEvent::FileSearchResult { query, matches } => { - self.chat_widget.apply_file_search_result(query, matches); + self.chat_widget.apply_file_search_result(&query, matches); } AppEvent::UpdateReasoningEffort(effort) => { self.on_update_reasoning_effort(effort); @@ -338,12 +336,12 @@ impl App { "failed to persist model selection" ); if let Some(profile) = profile { - self.chat_widget.add_error_message(format!( - "Failed to save model for profile `{profile}`: {err}" - )); + let message = + format!("Failed to save model for profile `{profile}`: {err}"); + self.chat_widget.add_error_message(&message); } else { - self.chat_widget - .add_error_message(format!("Failed to save default model: {err}")); + let message = format!("Failed to save default model: {err}"); + self.chat_widget.add_error_message(&message); } } } diff --git a/codex-rs/tui/src/app_backtrack.rs b/codex-rs/tui/src/app_backtrack.rs index c07be57af1..f1eaa3e840 100644 --- a/codex-rs/tui/src/app_backtrack.rs +++ b/codex-rs/tui/src/app_backtrack.rs @@ -45,7 +45,7 @@ impl App { kind: KeyEventKind::Press | KeyEventKind::Repeat, .. }) => { - self.overlay_step_backtrack(tui, event)?; + self.overlay_step_backtrack(tui, &event)?; Ok(true) } TuiEvent::Key(KeyEvent { @@ -58,7 +58,7 @@ impl App { } // Catchall: forward any other events to the overlay widget. _ => { - self.overlay_forward_event(tui, event)?; + self.overlay_forward_event(tui, &event)?; Ok(true) } } @@ -73,7 +73,7 @@ impl App { Ok(true) } else { // Not in backtrack mode: forward events to the overlay widget. - self.overlay_forward_event(tui, event)?; + self.overlay_forward_event(tui, &event)?; Ok(true) } } @@ -202,7 +202,7 @@ impl App { } /// Forward any event to the overlay and close it if done. - fn overlay_forward_event(&mut self, tui: &mut tui::Tui, event: TuiEvent) -> Result<()> { + fn overlay_forward_event(&mut self, tui: &mut tui::Tui, event: &TuiEvent) -> Result<()> { if let Some(overlay) = &mut self.overlay { overlay.handle_event(tui, event)?; if overlay.is_done() { @@ -233,7 +233,7 @@ impl App { } /// Handle Esc in overlay backtrack preview: step selection if armed, else forward. - fn overlay_step_backtrack(&mut self, tui: &mut tui::Tui, event: TuiEvent) -> Result<()> { + fn overlay_step_backtrack(&mut self, tui: &mut tui::Tui, event: &TuiEvent) -> Result<()> { if self.backtrack.base_id.is_some() { self.step_backtrack_and_highlight(tui); } else { @@ -344,7 +344,7 @@ impl App { self.trim_transcript_for_backtrack(nth_user_message); self.render_transcript_once(tui); if !prefill.is_empty() { - self.chat_widget.set_composer_text(prefill.to_string()); + self.chat_widget.set_composer_text(prefill); } tui.frame_requester().schedule_frame(); } diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index bf11ff571d..6d9380f185 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -162,7 +162,7 @@ impl ChatComposer { textarea_rect.width = textarea_rect.width.saturating_sub(LIVE_PREFIX_COLS); textarea_rect.x = textarea_rect.x.saturating_add(LIVE_PREFIX_COLS); let state = self.textarea_state.borrow(); - self.textarea.cursor_pos_with_state(textarea_rect, &state) + self.textarea.cursor_pos_with_state(textarea_rect, *state) } /// Returns true if the composer currently contains no user input. @@ -206,7 +206,7 @@ impl ChatComposer { let placeholder = format!("[Pasted Content {char_count} chars]"); self.textarea.insert_element(&placeholder); self.pending_pastes.push((placeholder, pasted)); - } else if char_count > 1 && self.handle_paste_image_path(pasted.clone()) { + } else if char_count > 1 && self.handle_paste_image_path(&pasted) { self.textarea.insert_str(" "); } else { self.textarea.insert_str(&pasted); @@ -224,8 +224,8 @@ impl ChatComposer { true } - pub fn handle_paste_image_path(&mut self, pasted: String) -> bool { - let Some(path_buf) = normalize_pasted_path(&pasted) else { + pub fn handle_paste_image_path(&mut self, pasted: &str) -> bool { + let Some(path_buf) = normalize_pasted_path(pasted) else { return false; }; @@ -252,12 +252,12 @@ impl ChatComposer { } /// Replace the entire composer content with `text` and reset cursor. - pub(crate) fn set_text_content(&mut self, text: String) { + pub(crate) fn set_text_content(&mut self, text: &str) { // Clear any existing content, placeholders, and attachments first. self.textarea.set_text(""); self.pending_pastes.clear(); self.attached_images.clear(); - self.textarea.set_text(&text); + self.textarea.set_text(text); self.textarea.set_cursor(0); self.sync_command_popup(); self.sync_file_search_popup(); @@ -297,19 +297,19 @@ impl ChatComposer { } /// Integrate results from an asynchronous file search. - pub(crate) fn on_file_search_result(&mut self, query: String, matches: Vec) { + pub(crate) fn on_file_search_result(&mut self, query: &str, matches: Vec) { // Only apply if user is still editing a token starting with `query`. let current_opt = Self::current_at_token(&self.textarea); let Some(current_token) = current_opt else { return; }; - if !current_token.starts_with(&query) { + if !current_token.starts_with(query) { return; } if let ActivePopup::File(popup) = &mut self.active_popup { - popup.set_matches(&query, matches); + popup.set_matches(query, matches); } } @@ -381,7 +381,7 @@ impl ChatComposer { // Ensure popup filtering/selection reflects the latest composer text // before applying completion. let first_line = self.textarea.text().lines().next().unwrap_or(""); - popup.on_composer_text_change(first_line.to_string()); + popup.on_composer_text_change(first_line); if let Some(sel) = popup.selected_item() { match sel { CommandItem::Builtin(cmd) => { @@ -420,9 +420,9 @@ impl ChatComposer { self.textarea.set_text(""); // Capture any needed data from popup before clearing it. let prompt_content = match sel { - CommandItem::UserPrompt(idx) => { - popup.prompt_content(idx).map(|s| s.to_string()) - } + CommandItem::UserPrompt(idx) => popup + .prompt_content(idx) + .map(std::string::ToString::to_string), _ => None, }; // Hide popup since an action has been dispatched. @@ -550,7 +550,7 @@ impl ChatComposer { let format_label = match Path::new(&sel_path) .extension() .and_then(|e| e.to_str()) - .map(|s| s.to_ascii_lowercase()) + .map(str::to_ascii_lowercase) { Some(ext) if ext == "png" => "PNG", Some(ext) if ext == "jpg" || ext == "jpeg" => "JPEG", @@ -617,7 +617,7 @@ impl ChatComposer { text[safe_cursor..] .chars() .next() - .map(|c| c.is_whitespace()) + .map(char::is_whitespace) .unwrap_or(false) } else { false @@ -645,7 +645,7 @@ impl ChatComposer { let ws_len_right: usize = after_cursor .chars() .take_while(|c| c.is_whitespace()) - .map(|c| c.len_utf8()) + .map(char::len_utf8) .sum(); let start_right = safe_cursor + ws_len_right; let end_right_rel = text[start_right..] @@ -910,7 +910,8 @@ impl ChatComposer { if !grab.grabbed.is_empty() { self.textarea.replace_range(grab.start_byte..safe_cur, ""); } - self.paste_burst.begin_with_retro_grabbed(grab.grabbed, now); + self.paste_burst + .begin_with_retro_grabbed(&grab.grabbed, now); self.paste_burst.append_char_to_buffer(ch, now); return (InputResult::None, true); } @@ -1092,9 +1093,8 @@ impl ChatComposer { .nth(occ_before) { break 'out Some((remove_idx, ph.clone())); - } else { - break 'out Some((i, ph.clone())); } + break 'out Some((i, ph.clone())); } None }; @@ -1150,7 +1150,7 @@ impl ChatComposer { match &mut self.active_popup { ActivePopup::Command(popup) => { if input_starts_with_slash { - popup.on_composer_text_change(first_line.to_string()); + popup.on_composer_text_change(first_line); } else { self.active_popup = ActivePopup::None; } @@ -1158,7 +1158,7 @@ impl ChatComposer { _ => { if input_starts_with_slash { let mut command_popup = CommandPopup::new(self.custom_prompts.clone()); - command_popup.on_composer_text_change(first_line.to_string()); + command_popup.on_composer_text_change(first_line); self.active_popup = ActivePopup::Command(command_popup); } } diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs index a492a2e17e..9888a5c22b 100644 --- a/codex-rs/tui/src/bottom_pane/command_popup.rs +++ b/codex-rs/tui/src/bottom_pane/command_popup.rs @@ -65,7 +65,7 @@ impl CommandPopup { /// passed in is expected to start with a leading '/'. Everything after the /// *first* '/" on the *first* line becomes the active filter that is used /// to narrow down the list of available commands. - pub(crate) fn on_composer_text_change(&mut self, text: String) { + pub(crate) fn on_composer_text_change(&mut self, text: &str) { let first_line = text.lines().next().unwrap_or(""); if let Some(stripped) = first_line.strip_prefix('/') { @@ -224,7 +224,7 @@ mod tests { let mut popup = CommandPopup::new(Vec::new()); // Simulate the composer line starting with '/in' so the popup filters // matching commands by prefix. - popup.on_composer_text_change("/in".to_string()); + popup.on_composer_text_change("/in"); // Access the filtered list via the selected command and ensure that // one of the matches is the new "init" command. @@ -242,7 +242,7 @@ mod tests { #[test] fn selecting_init_by_exact_match() { let mut popup = CommandPopup::new(Vec::new()); - popup.on_composer_text_change("/init".to_string()); + popup.on_composer_text_change("/init"); // When an exact match exists, the selected command should be that // command by default. @@ -257,7 +257,7 @@ mod tests { #[test] fn model_is_first_suggestion_for_mo() { let mut popup = CommandPopup::new(Vec::new()); - popup.on_composer_text_change("/mo".to_string()); + popup.on_composer_text_change("/mo"); let matches = popup.filtered_items(); match matches.first() { Some(CommandItem::Builtin(cmd)) => assert_eq!(cmd.command(), "model"), @@ -287,7 +287,9 @@ mod tests { let mut prompt_names: Vec = items .into_iter() .filter_map(|it| match it { - CommandItem::UserPrompt(i) => popup.prompt_name(i).map(|s| s.to_string()), + CommandItem::UserPrompt(i) => { + popup.prompt_name(i).map(std::string::ToString::to_string) + } _ => None, }) .collect(); diff --git a/codex-rs/tui/src/bottom_pane/custom_prompt_view.rs b/codex-rs/tui/src/bottom_pane/custom_prompt_view.rs index 4bad707724..3a904848a7 100644 --- a/codex-rs/tui/src/bottom_pane/custom_prompt_view.rs +++ b/codex-rs/tui/src/bottom_pane/custom_prompt_view.rs @@ -237,7 +237,7 @@ impl BottomPaneView for CustomPromptView { height: text_area_height, }; let state = self.textarea_state.borrow(); - self.textarea.cursor_pos_with_state(textarea_rect, &state) + self.textarea.cursor_pos_with_state(textarea_rect, *state) } } diff --git a/codex-rs/tui/src/bottom_pane/mod.rs b/codex-rs/tui/src/bottom_pane/mod.rs index 06eff112bc..0abaa8700f 100644 --- a/codex-rs/tui/src/bottom_pane/mod.rs +++ b/codex-rs/tui/src/bottom_pane/mod.rs @@ -201,7 +201,7 @@ impl BottomPane { return if self.composer_is_empty() { CancellationEvent::NotHandled } else { - self.set_composer_text(String::new()); + self.set_composer_text(""); self.show_ctrl_c_quit_hint(); CancellationEvent::Handled }; @@ -250,7 +250,7 @@ impl BottomPane { } /// Replace the composer text with `text`. - pub(crate) fn set_composer_text(&mut self, text: String) { + pub(crate) fn set_composer_text(&mut self, text: &str) { self.composer.set_text_content(text); self.request_redraw(); } @@ -452,7 +452,7 @@ impl BottomPane { } } - pub(crate) fn on_file_search_result(&mut self, query: String, matches: Vec) { + pub(crate) fn on_file_search_result(&mut self, query: &str, matches: Vec) { self.composer.on_file_search_result(query, matches); self.request_redraw(); } diff --git a/codex-rs/tui/src/bottom_pane/paste_burst.rs b/codex-rs/tui/src/bottom_pane/paste_burst.rs index d507dcae37..67b8490d38 100644 --- a/codex-rs/tui/src/bottom_pane/paste_burst.rs +++ b/codex-rs/tui/src/bottom_pane/paste_burst.rs @@ -149,9 +149,9 @@ impl PasteBurst { } /// Begin buffering with retroactively grabbed text. - pub fn begin_with_retro_grabbed(&mut self, grabbed: String, now: Instant) { + pub fn begin_with_retro_grabbed(&mut self, grabbed: &str, now: Instant) { if !grabbed.is_empty() { - self.buffer.push_str(&grabbed); + self.buffer.push_str(grabbed); } self.active = true; self.burst_window_until = Some(now + PASTE_ENTER_SUPPRESS_WINDOW); @@ -183,10 +183,10 @@ impl PasteBurst { let start_byte = retro_start_index(before, retro_chars); let grabbed = before[start_byte..].to_string(); let looks_pastey = - grabbed.chars().any(|c| c.is_whitespace()) || grabbed.chars().count() >= 16; + grabbed.chars().any(char::is_whitespace) || grabbed.chars().count() >= 16; if looks_pastey { // Note: caller is responsible for removing this slice from UI text. - self.begin_with_retro_grabbed(grabbed.clone(), now); + self.begin_with_retro_grabbed(&grabbed, now); Some(RetroGrab { start_byte, grabbed, diff --git a/codex-rs/tui/src/bottom_pane/textarea.rs b/codex-rs/tui/src/bottom_pane/textarea.rs index 9166b8c7f6..269fa345e0 100644 --- a/codex-rs/tui/src/bottom_pane/textarea.rs +++ b/codex-rs/tui/src/bottom_pane/textarea.rs @@ -132,11 +132,11 @@ impl TextArea { #[cfg_attr(not(test), allow(dead_code))] pub fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> { - self.cursor_pos_with_state(area, &TextAreaState::default()) + self.cursor_pos_with_state(area, TextAreaState::default()) } /// Compute the on-screen cursor position taking scrolling into account. - pub fn cursor_pos_with_state(&self, area: Rect, state: &TextAreaState) -> Option<(u16, u16)> { + pub fn cursor_pos_with_state(&self, area: Rect, state: TextAreaState) -> Option<(u16, u16)> { let lines = self.wrapped_lines(area.width); let effective_scroll = self.effective_scroll(area.height, &lines, state.scroll); let i = Self::wrapped_line_index_by_start(&lines, self.cursor_pos)?; @@ -1408,7 +1408,7 @@ mod tests { let mut state = TextAreaState::default(); let small_area = Rect::new(0, 0, 6, 1); // First call: cursor not visible -> effective scroll ensures it is - let (_x, y) = t.cursor_pos_with_state(small_area, &state).unwrap(); + let (_x, y) = t.cursor_pos_with_state(small_area, state).unwrap(); assert_eq!(y, 0); // Render with state to update actual scroll value @@ -1429,7 +1429,7 @@ mod tests { // effective scroll is 0 and the cursor position matches cursor_pos. let bad_state = TextAreaState { scroll: 999 }; let (x1, y1) = t.cursor_pos(area).unwrap(); - let (x2, y2) = t.cursor_pos_with_state(area, &bad_state).unwrap(); + let (x2, y2) = t.cursor_pos_with_state(area, bad_state).unwrap(); assert_eq!((x2, y2), (x1, y1)); // Case 2: Cursor below the current window — y should be clamped to the @@ -1442,7 +1442,7 @@ mod tests { t.set_cursor(t.text().len().saturating_sub(2)); let small_area = Rect::new(0, 0, wrap_width, 2); let state = TextAreaState { scroll: 0 }; - let (_x, y) = t.cursor_pos_with_state(small_area, &state).unwrap(); + let (_x, y) = t.cursor_pos_with_state(small_area, state).unwrap(); assert_eq!(y, small_area.y + small_area.height - 1); // Case 3: Cursor above the current window — y should be top row (0) @@ -1456,7 +1456,7 @@ mod tests { let state = TextAreaState { scroll: lines.saturating_mul(2), }; - let (_x, y) = t.cursor_pos_with_state(area, &state).unwrap(); + let (_x, y) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!(y, area.y); } @@ -1480,7 +1480,7 @@ mod tests { // With state and small height, cursor should be visible at row 0, col 0 let small_area = Rect::new(0, 0, 4, 1); let state = TextAreaState::default(); - let (x, y) = t.cursor_pos_with_state(small_area, &state).unwrap(); + let (x, y) = t.cursor_pos_with_state(small_area, state).unwrap(); assert_eq!((x, y), (0, 0)); // Place cursor in the middle of the second wrapped line ("efgh"), at 'g' @@ -1510,35 +1510,35 @@ mod tests { // Start at beginning t.set_cursor(0); ratatui::widgets::StatefulWidgetRef::render_ref(&(&t), area, &mut buf, &mut state); - let (x, y) = t.cursor_pos_with_state(area, &state).unwrap(); + let (x, y) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!((x, y), (0, 0)); // Move down to second visual line; should be at bottom row (row 1) within 2-line viewport t.move_cursor_down(); ratatui::widgets::StatefulWidgetRef::render_ref(&(&t), area, &mut buf, &mut state); - let (x, y) = t.cursor_pos_with_state(area, &state).unwrap(); + let (x, y) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!((x, y), (0, 1)); // Move down to third visual line; viewport scrolls and keeps cursor on bottom row t.move_cursor_down(); ratatui::widgets::StatefulWidgetRef::render_ref(&(&t), area, &mut buf, &mut state); - let (x, y) = t.cursor_pos_with_state(area, &state).unwrap(); + let (x, y) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!((x, y), (0, 1)); // Move up to second visual line; with current scroll, it appears on top row t.move_cursor_up(); ratatui::widgets::StatefulWidgetRef::render_ref(&(&t), area, &mut buf, &mut state); - let (x, y) = t.cursor_pos_with_state(area, &state).unwrap(); + let (x, y) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!((x, y), (0, 0)); // Column preservation across moves: set to col 2 on first line, move down t.set_cursor(2); ratatui::widgets::StatefulWidgetRef::render_ref(&(&t), area, &mut buf, &mut state); - let (x0, y0) = t.cursor_pos_with_state(area, &state).unwrap(); + let (x0, y0) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!((x0, y0), (2, 0)); t.move_cursor_down(); ratatui::widgets::StatefulWidgetRef::render_ref(&(&t), area, &mut buf, &mut state); - let (x1, y1) = t.cursor_pos_with_state(area, &state).unwrap(); + let (x1, y1) = t.cursor_pos_with_state(area, state).unwrap(); assert_eq!((x1, y1), (2, 1)); } @@ -1796,7 +1796,7 @@ mod tests { // cursor_pos_with_state: always within viewport rows let (_x, _y) = ta - .cursor_pos_with_state(area, &state) + .cursor_pos_with_state(area, state) .unwrap_or((area.x, area.y)); // Stateful render should not panic, and updates scroll diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index b4204f3a37..193ed302a9 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -248,22 +248,22 @@ impl ChatWidget { } } - fn on_agent_message(&mut self, message: String) { + fn on_agent_message(&mut self, message: &str) { let sink = AppEventHistorySink(self.app_event_tx.clone()); - let finished = self.stream.apply_final_answer(&message, &sink); + let finished = self.stream.apply_final_answer(message, &sink); self.handle_if_stream_finished(finished); self.request_redraw(); } - fn on_agent_message_delta(&mut self, delta: String) { + fn on_agent_message_delta(&mut self, delta: &str) { self.handle_streaming_delta(delta); } - fn on_agent_reasoning_delta(&mut self, delta: String) { + fn on_agent_reasoning_delta(&mut self, delta: &str) { // For reasoning deltas, do not stream to history. Accumulate the // current reasoning block and extract the first bold element // (between **/**) as the chunk header. Show this header as status. - self.reasoning_buffer.push_str(&delta); + self.reasoning_buffer.push_str(delta); if let Some(header) = extract_first_bold(&self.reasoning_buffer) { // Update the shimmer header to the extracted reasoning chunk header. @@ -279,7 +279,7 @@ impl ChatWidget { self.full_reasoning_buffer.push_str(&self.reasoning_buffer); if !self.full_reasoning_buffer.is_empty() { let cell = history_cell::new_reasoning_summary_block( - self.full_reasoning_buffer.clone(), + &self.full_reasoning_buffer, &self.config, ); self.add_boxed_history(cell); @@ -340,7 +340,7 @@ impl ChatWidget { self.rate_limit_snapshot = Some(snapshot); if !warnings.is_empty() { for warning in warnings { - self.add_to_history(history_cell::new_warning_event(warning)); + self.add_to_history(history_cell::new_warning_event(&warning)); } self.request_redraw(); } @@ -356,7 +356,7 @@ impl ChatWidget { self.stream.clear_all(); } - fn on_error(&mut self, message: String) { + fn on_error(&mut self, message: &str) { self.finalize_turn(); self.add_to_history(history_cell::new_error_event(message)); self.request_redraw(); @@ -368,13 +368,13 @@ impl ChatWidget { /// Handle a turn aborted due to user interrupt (Esc). /// When there are queued user messages, restore them into the composer /// separated by newlines rather than auto‑submitting the next one. - fn on_interrupted_turn(&mut self, reason: TurnAbortReason) { + fn on_interrupted_turn(&mut self, reason: &TurnAbortReason) { // Finalize, log a gentle prompt, and clear running state. self.finalize_turn(); - if reason != TurnAbortReason::ReviewEnded { + if *reason != TurnAbortReason::ReviewEnded { self.add_to_history(history_cell::new_error_event( - "Conversation interrupted - tell the model what to do differently".to_owned(), + "Conversation interrupted - tell the model what to do differently", )); } @@ -386,7 +386,7 @@ impl ChatWidget { .map(|m| m.text.clone()) .collect::>() .join("\n"); - self.bottom_pane.set_composer_text(combined); + self.bottom_pane.set_composer_text(&combined); // Clear the queue and update the status indicator list. self.queued_user_messages.clear(); self.refresh_queued_user_messages(); @@ -450,7 +450,7 @@ impl ChatWidget { fn on_exec_command_end(&mut self, ev: ExecCommandEndEvent) { let ev2 = ev.clone(); - self.defer_or_handle(|q| q.push_exec_end(ev), |s| s.handle_exec_end_now(ev2)); + self.defer_or_handle(|q| q.push_exec_end(ev), |s| s.handle_exec_end_now(&ev2)); } fn on_mcp_tool_call_begin(&mut self, ev: McpToolCallBeginEvent) { @@ -467,7 +467,7 @@ impl ChatWidget { self.flush_answer_stream_with_separator(); } - fn on_web_search_end(&mut self, ev: WebSearchEndEvent) { + fn on_web_search_end(&mut self, ev: &WebSearchEndEvent) { self.flush_answer_stream_with_separator(); self.add_to_history(history_cell::new_web_search_call(format!( "Searched: {}", @@ -492,11 +492,11 @@ impl ChatWidget { self.app_event_tx.send(AppEvent::ExitRequest); } - fn on_turn_diff(&mut self, unified_diff: String) { + fn on_turn_diff(&mut self, unified_diff: &str) { debug!("TurnDiffEvent: {unified_diff}"); } - fn on_background_event(&mut self, message: String) { + fn on_background_event(&mut self, message: &str) { debug!("BackgroundEvent: {message}"); } @@ -551,16 +551,16 @@ impl ChatWidget { } #[inline] - fn handle_streaming_delta(&mut self, delta: String) { + fn handle_streaming_delta(&mut self, delta: &str) { // Before streaming agent content, flush any active exec cell group. self.flush_active_exec_cell(); let sink = AppEventHistorySink(self.app_event_tx.clone()); self.stream.begin(&sink); - self.stream.push_and_maybe_commit(&delta, &sink); + self.stream.push_and_maybe_commit(delta, &sink); self.request_redraw(); } - pub(crate) fn handle_exec_end_now(&mut self, ev: ExecCommandEndEvent) { + pub(crate) fn handle_exec_end_now(&mut self, ev: &ExecCommandEndEvent) { let running = self.running_commands.remove(&ev.call_id); let (command, parsed) = match running { Some(rc) => (rc.command, rc.parsed_cmd), @@ -608,7 +608,7 @@ impl ChatWidget { self.flush_answer_stream_with_separator(); // Emit the proposed command into history (like proposed patches) self.add_to_history(history_cell::new_proposed_command(&ev.command)); - let command = shlex::try_join(ev.command.iter().map(|s| s.as_str())) + let command = shlex::try_join(ev.command.iter().map(std::string::String::as_str)) .unwrap_or_else(|_| ev.command.join(" ")); self.notify(Notification::ExecApprovalRequested { command }); @@ -881,7 +881,7 @@ impl ChatWidget { } if !self.queued_user_messages.is_empty() => { // Prefer the most recently queued item. if let Some(user_message) = self.queued_user_messages.pop_back() { - self.bottom_pane.set_composer_text(user_message.text); + self.bottom_pane.set_composer_text(&user_message.text); self.refresh_queued_user_messages(); self.request_redraw(); } @@ -931,7 +931,7 @@ impl ChatWidget { "'/{}' is disabled while a task is in progress.", cmd.command() ); - self.add_to_history(history_cell::new_error_event(message)); + self.add_to_history(history_cell::new_error_event(&message)); self.request_redraw(); return; } @@ -1040,7 +1040,7 @@ impl ChatWidget { } // Returns true if caller should skip rendering this frame (a future frame is scheduled). - pub(crate) fn handle_paste_burst_tick(&mut self, frame_requester: FrameRequester) -> bool { + pub(crate) fn handle_paste_burst_tick(&mut self, frame_requester: &FrameRequester) -> bool { if self.bottom_pane.flush_paste_burst_if_due() { // A paste just flushed; request an immediate redraw and skip this frame. self.request_redraw(); @@ -1150,17 +1150,19 @@ impl ChatWidget { match msg { EventMsg::SessionConfigured(e) => self.on_session_configured(e), - EventMsg::AgentMessage(AgentMessageEvent { message }) => self.on_agent_message(message), + EventMsg::AgentMessage(AgentMessageEvent { message }) => { + self.on_agent_message(&message) + } EventMsg::AgentMessageDelta(AgentMessageDeltaEvent { delta }) => { - self.on_agent_message_delta(delta) + self.on_agent_message_delta(&delta) } EventMsg::AgentReasoningDelta(AgentReasoningDeltaEvent { delta }) | EventMsg::AgentReasoningRawContentDelta(AgentReasoningRawContentDeltaEvent { delta, - }) => self.on_agent_reasoning_delta(delta), + }) => self.on_agent_reasoning_delta(&delta), EventMsg::AgentReasoning(AgentReasoningEvent { .. }) => self.on_agent_reasoning_final(), EventMsg::AgentReasoningRawContent(AgentReasoningRawContentEvent { text }) => { - self.on_agent_reasoning_delta(text); + self.on_agent_reasoning_delta(&text); self.on_agent_reasoning_final() } EventMsg::AgentReasoningSectionBreak(_) => self.on_reasoning_section_break(), @@ -1172,18 +1174,15 @@ impl ChatWidget { self.set_token_info(ev.info); self.on_rate_limit_snapshot(ev.rate_limits); } - EventMsg::Error(ErrorEvent { message }) => self.on_error(message), - EventMsg::TurnAborted(ev) => match ev.reason { - TurnAbortReason::Interrupted => { - self.on_interrupted_turn(ev.reason); + EventMsg::Error(ErrorEvent { message }) => self.on_error(&message), + EventMsg::TurnAborted(ev) => { + let reason = ev.reason; + if reason == TurnAbortReason::Replaced { + self.on_error("Turn aborted: replaced by a new task") + } else { + self.on_interrupted_turn(&reason); } - TurnAbortReason::Replaced => { - self.on_error("Turn aborted: replaced by a new task".to_owned()) - } - TurnAbortReason::ReviewEnded => { - self.on_interrupted_turn(ev.reason); - } - }, + } EventMsg::PlanUpdate(update) => self.on_plan_update(update), EventMsg::ExecApprovalRequest(ev) => { // For replayed events, synthesize an empty id (these should not occur). @@ -1200,19 +1199,19 @@ impl ChatWidget { EventMsg::McpToolCallBegin(ev) => self.on_mcp_tool_call_begin(ev), EventMsg::McpToolCallEnd(ev) => self.on_mcp_tool_call_end(ev), EventMsg::WebSearchBegin(ev) => self.on_web_search_begin(ev), - EventMsg::WebSearchEnd(ev) => self.on_web_search_end(ev), + EventMsg::WebSearchEnd(ev) => self.on_web_search_end(&ev), EventMsg::GetHistoryEntryResponse(ev) => self.on_get_history_entry_response(ev), - EventMsg::McpListToolsResponse(ev) => self.on_list_mcp_tools(ev), + EventMsg::McpListToolsResponse(ev) => self.on_list_mcp_tools(&ev), EventMsg::ListCustomPromptsResponse(ev) => self.on_list_custom_prompts(ev), EventMsg::ShutdownComplete => self.on_shutdown_complete(), - EventMsg::TurnDiff(TurnDiffEvent { unified_diff }) => self.on_turn_diff(unified_diff), + EventMsg::TurnDiff(TurnDiffEvent { unified_diff }) => self.on_turn_diff(&unified_diff), EventMsg::BackgroundEvent(BackgroundEventEvent { message }) => { - self.on_background_event(message) + self.on_background_event(&message) } EventMsg::StreamError(StreamErrorEvent { message }) => self.on_stream_error(message), EventMsg::UserMessage(ev) => { if from_replay { - self.on_user_message_event(ev); + self.on_user_message_event(&ev); } } EventMsg::ConversationPath(ev) => { @@ -1220,16 +1219,19 @@ impl ChatWidget { .send(crate::app_event::AppEvent::ConversationHistory(ev)); } EventMsg::EnteredReviewMode(review_request) => { - self.on_entered_review_mode(review_request) + self.on_entered_review_mode(&review_request) } EventMsg::ExitedReviewMode(review) => self.on_exited_review_mode(review), } } - fn on_entered_review_mode(&mut self, review: ReviewRequest) { + fn on_entered_review_mode(&mut self, review: &ReviewRequest) { // Enter review mode and emit a concise banner self.is_review_mode = true; - let banner = format!(">> Code review started: {} <<", review.user_facing_hint); + let banner = format!( + ">> Code review started: {} <<", + review.user_facing_hint.as_str() + ); self.add_to_history(history_cell::new_review_status_line(banner)); self.request_redraw(); } @@ -1246,7 +1248,7 @@ impl ChatWidget { if explanation.is_empty() { tracing::error!("Reviewer failed to output a response."); self.add_to_history(history_cell::new_error_event( - "Reviewer failed to output a response.".to_owned(), + "Reviewer failed to output a response.", )); } else { // Show explanation when there are no structured findings. @@ -1275,8 +1277,8 @@ impl ChatWidget { self.request_redraw(); } - fn on_user_message_event(&mut self, event: UserMessageEvent) { - match event.kind { + fn on_user_message_event(&mut self, event: &UserMessageEvent) { + match event.kind.as_ref() { Some(InputMessageKind::EnvironmentContext) | Some(InputMessageKind::UserInstructions) => { // Skip XML‑wrapped context blocks in the transcript. @@ -1503,7 +1505,7 @@ impl ChatWidget { self.request_redraw(); } - pub(crate) fn add_error_message(&mut self, message: String) { + pub(crate) fn add_error_message(&mut self, message: &str) { self.add_to_history(history_cell::new_error_event(message)); self.request_redraw(); } @@ -1517,7 +1519,7 @@ impl ChatWidget { } /// Forward file-search results to the bottom pane. - pub(crate) fn apply_file_search_result(&mut self, query: String, matches: Vec) { + pub(crate) fn apply_file_search_result(&mut self, query: &str, matches: Vec) { self.bottom_pane.on_file_search_result(query, matches); } @@ -1552,7 +1554,7 @@ impl ChatWidget { } /// Replace the composer content with the provided text and reset cursor. - pub(crate) fn set_composer_text(&mut self, text: String) { + pub(crate) fn set_composer_text(&mut self, text: &str) { self.bottom_pane.set_composer_text(text); } @@ -1572,8 +1574,8 @@ impl ChatWidget { } } - fn on_list_mcp_tools(&mut self, ev: McpListToolsResponseEvent) { - self.add_to_history(history_cell::new_mcp_tools_output(&self.config, ev.tools)); + fn on_list_mcp_tools(&mut self, ev: &McpListToolsResponseEvent) { + self.add_to_history(history_cell::new_mcp_tools_output(&self.config, &ev.tools)); } fn on_list_custom_prompts(&mut self, ev: ListCustomPromptsResponseEvent) { @@ -1900,9 +1902,8 @@ fn extract_first_bold(s: &str) -> Option { let trimmed = inner.trim(); if !trimmed.is_empty() { return Some(trimmed.to_string()); - } else { - return None; } + return None; } j += 1; } diff --git a/codex-rs/tui/src/chatwidget/interrupts.rs b/codex-rs/tui/src/chatwidget/interrupts.rs index 531de3e646..46e11f7fee 100644 --- a/codex-rs/tui/src/chatwidget/interrupts.rs +++ b/codex-rs/tui/src/chatwidget/interrupts.rs @@ -79,7 +79,7 @@ impl InterruptManager { chat.handle_apply_patch_approval_now(id, ev) } QueuedInterrupt::ExecBegin(ev) => chat.handle_exec_begin_now(ev), - QueuedInterrupt::ExecEnd(ev) => chat.handle_exec_end_now(ev), + QueuedInterrupt::ExecEnd(ev) => chat.handle_exec_end_now(&ev), QueuedInterrupt::McpBegin(ev) => chat.handle_mcp_begin_now(ev), QueuedInterrupt::McpEnd(ev) => chat.handle_mcp_end_now(ev), QueuedInterrupt::PatchEnd(ev) => chat.handle_patch_apply_end_now(ev), diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index c009cabd94..b9a3be49fe 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -451,9 +451,9 @@ fn sample_rate_limit_snapshot( } } -fn capture_limits_snapshot(snapshot: Option) -> String { +fn capture_limits_snapshot(snapshot: Option<&RateLimitSnapshotEvent>) -> String { let lines = match snapshot { - Some(ref snapshot) => history_cell::new_limits_output(snapshot).display_lines(80), + Some(snapshot) => history_cell::new_limits_output(snapshot).display_lines(80), None => history_cell::new_limits_unavailable().display_lines(80), }; styled_lines_to_string(&lines) @@ -467,25 +467,25 @@ fn limits_placeholder() { #[test] fn limits_snapshot_basic() { - let visual = capture_limits_snapshot(Some(sample_rate_limit_snapshot(30.0, 60.0, 40.0))); + let visual = capture_limits_snapshot(Some(&sample_rate_limit_snapshot(30.0, 60.0, 40.0))); assert_snapshot!(visual); } #[test] fn limits_snapshot_hourly_remaining() { - let visual = capture_limits_snapshot(Some(sample_rate_limit_snapshot(0.0, 20.0, 10.0))); + let visual = capture_limits_snapshot(Some(&sample_rate_limit_snapshot(0.0, 20.0, 10.0))); assert_snapshot!(visual); } #[test] fn limits_snapshot_mixed_usage() { - let visual = capture_limits_snapshot(Some(sample_rate_limit_snapshot(20.0, 20.0, 10.0))); + let visual = capture_limits_snapshot(Some(&sample_rate_limit_snapshot(20.0, 20.0, 10.0))); assert_snapshot!(visual); } #[test] fn limits_snapshot_weekly_heavy() { - let visual = capture_limits_snapshot(Some(sample_rate_limit_snapshot(98.0, 0.0, 10.0))); + let visual = capture_limits_snapshot(Some(&sample_rate_limit_snapshot(98.0, 0.0, 10.0))); assert_snapshot!(visual); } @@ -1170,7 +1170,10 @@ async fn binary_size_transcript_snapshot() { call_id: e.call_id.clone(), command: e.command, cwd: e.cwd, - parsed_cmd: parsed_cmd.into_iter().map(|c| c.into()).collect(), + parsed_cmd: parsed_cmd + .into_iter() + .map(std::convert::Into::into) + .collect(), }), } } @@ -1191,7 +1194,7 @@ async fn binary_size_transcript_snapshot() { crate::insert_history::insert_history_lines_to_writer( &mut terminal, &mut ansi, - lines, + &lines, ); } } @@ -1216,7 +1219,7 @@ async fn binary_size_transcript_snapshot() { crate::insert_history::insert_history_lines_to_writer( &mut terminal, &mut ansi, - lines, + &lines, ); } } @@ -1247,7 +1250,7 @@ async fn binary_size_transcript_snapshot() { // Trim trailing spaces to match plain text fixture lines.push(s.trim_end().to_string()); } - while lines.last().is_some_and(|l| l.is_empty()) { + while lines.last().is_some_and(std::string::String::is_empty) { lines.pop(); } // Consider content only after the last session banner marker. Skip the transient @@ -2181,7 +2184,7 @@ fn chatwidget_exec_and_status_layout_vt100_snapshot() { }), }); chat.bottom_pane - .set_composer_text("Summarize recent commits".to_string()); + .set_composer_text("Summarize recent commits"); chat.handle_codex_event(Event { id: "t1".into(), msg: EventMsg::AgentMessage(AgentMessageEvent { message: "I’m going to search the repo for where “Change Approved” is rendered to update that view.".into() }), @@ -2202,7 +2205,7 @@ fn chatwidget_exec_and_status_layout_vt100_snapshot() { // 1) Apply any pending history insertions by emitting ANSI to a buffer via insert_history_lines_to_writer let mut ansi: Vec = Vec::new(); for lines in drain_insert_history(&mut rx) { - crate::insert_history::insert_history_lines_to_writer(&mut term, &mut ansi, lines); + crate::insert_history::insert_history_lines_to_writer(&mut term, &mut ansi, &lines); } // 2) Render the ChatWidget UI into an off-screen buffer using WidgetRef directly @@ -2318,7 +2321,7 @@ printf 'fenced within fenced\n' if let AppEvent::InsertHistoryCell(cell) = app_ev { let lines = cell.display_lines(width); crate::insert_history::insert_history_lines_to_writer( - &mut term, &mut ansi, lines, + &mut term, &mut ansi, &lines, ); inserted_any = true; } @@ -2337,7 +2340,7 @@ printf 'fenced within fenced\n' }), }); for lines in drain_insert_history(&mut rx) { - crate::insert_history::insert_history_lines_to_writer(&mut term, &mut ansi, lines); + crate::insert_history::insert_history_lines_to_writer(&mut term, &mut ansi, &lines); } let mut parser = vt100::Parser::new(height, width, 0); diff --git a/codex-rs/tui/src/clipboard_paste.rs b/codex-rs/tui/src/clipboard_paste.rs index 82a140453a..4945eea350 100644 --- a/codex-rs/tui/src/clipboard_paste.rs +++ b/codex-rs/tui/src/clipboard_paste.rs @@ -198,7 +198,7 @@ pub fn pasted_image_format(path: &Path) -> EncodedImageFormat { match path .extension() .and_then(|e| e.to_str()) - .map(|s| s.to_ascii_lowercase()) + .map(str::to_ascii_lowercase) .as_deref() { Some("png") => EncodedImageFormat::Png, diff --git a/codex-rs/tui/src/diff_render.rs b/codex-rs/tui/src/diff_render.rs index 2787f76607..53976fe057 100644 --- a/codex-rs/tui/src/diff_render.rs +++ b/codex-rs/tui/src/diff_render.rs @@ -16,6 +16,7 @@ use codex_core::protocol::FileChange; const SPACES_AFTER_LINE_NUMBER: usize = 6; // Internal representation for diff line rendering +#[derive(Clone, Copy)] enum DiffLineType { Insert, Delete, @@ -80,6 +81,7 @@ fn collect_rows(changes: &HashMap) -> Vec { rows } +#[derive(Clone, Copy)] enum HeaderKind { ProposedChange, Edited, @@ -283,7 +285,7 @@ fn calculate_add_remove_from_diff(diff: &str) -> (usize, usize) { patch .hunks() .iter() - .flat_map(|h| h.lines()) + .flat_map(diffy::Hunk::lines) .fold((0, 0), |(a, d), l| match l { diffy::Line::Insert(_) => (a + 1, d), diffy::Line::Delete(_) => (a, d + 1), diff --git a/codex-rs/tui/src/exec_command.rs b/codex-rs/tui/src/exec_command.rs index 93c937e0d1..993e5813e8 100644 --- a/codex-rs/tui/src/exec_command.rs +++ b/codex-rs/tui/src/exec_command.rs @@ -5,7 +5,7 @@ use dirs::home_dir; use shlex::try_join; pub(crate) fn escape_command(command: &[String]) -> String { - try_join(command.iter().map(|s| s.as_str())).unwrap_or_else(|_| command.join(" ")) + try_join(command.iter().map(std::string::String::as_str)).unwrap_or_else(|_| command.join(" ")) } pub(crate) fn strip_bash_lc_and_escape(command: &[String]) -> String { diff --git a/codex-rs/tui/src/file_search.rs b/codex-rs/tui/src/file_search.rs index 7f15e46abb..43320fd71f 100644 --- a/codex-rs/tui/src/file_search.rs +++ b/codex-rs/tui/src/file_search.rs @@ -79,7 +79,7 @@ impl FileSearchManager { } /// Call whenever the user edits the `@` token. - pub fn on_user_query(&self, query: String) { + pub fn on_user_query(&self, query: &str) { { #[expect(clippy::unwrap_used)] let mut st = self.state.lock().unwrap(); @@ -90,7 +90,7 @@ impl FileSearchManager { // Update latest query. st.latest_query.clear(); - st.latest_query.push_str(&query); + st.latest_query.push_str(query); // If there is an in-flight search that is definitely obsolete, // cancel it now. @@ -170,7 +170,7 @@ impl FileSearchManager { &search_dir, Vec::new(), NUM_FILE_SEARCH_THREADS, - cancellation_token.clone(), + &cancellation_token, compute_indices, ) .map(|res| res.matches) diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 24e6b2844f..d40e0e0c4f 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -66,7 +66,7 @@ pub(crate) struct CommandOutput { pub(crate) formatted_output: String, } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum PatchEventType { ApprovalRequest, ApplyBegin { auto_approved: bool }, @@ -270,12 +270,7 @@ pub(crate) struct PatchHistoryCell { impl HistoryCell for PatchHistoryCell { fn display_lines(&self, width: u16) -> Vec> { - create_diff_summary( - &self.changes, - self.event_type.clone(), - &self.cwd, - width as usize, - ) + create_diff_summary(&self.changes, self.event_type, &self.cwd, width as usize) } } @@ -390,23 +385,19 @@ impl ExecCell { .iter() .all(|c| matches!(c, ParsedCommand::Read { .. })) { - let names: Vec = call - .parsed - .iter() - .map(|c| match c { - ParsedCommand::Read { name, .. } => name.clone(), - _ => unreachable!(), - }) - .unique() - .collect(); - vec![( - "Read", - itertools::Itertools::intersperse( - names.into_iter().map(|n| n.into()), - ", ".dim(), - ) - .collect(), - )] + let spans = itertools::Itertools::intersperse( + call.parsed + .iter() + .filter_map(|c| match c { + ParsedCommand::Read { name, .. } => Some(name.clone()), + _ => None, + }) + .unique() + .map(std::convert::Into::into), + ", ".dim(), + ) + .collect(); + vec![("Read", spans)] } else { let mut lines = Vec::new(); for p in call.parsed { @@ -449,7 +440,13 @@ impl ExecCell { push_owned_lines(&wrapped, &mut out_indented); } } - out.extend(prefix_lines(out_indented, " └ ".dim(), " ".into())); + let initial_prefix = " └ ".dim(); + let subsequent_prefix = " ".into(); + out.extend(prefix_lines( + out_indented, + &initial_prefix, + &subsequent_prefix, + )); out } @@ -501,16 +498,12 @@ impl ExecCell { if let Some(output) = call.output.as_ref() && output.exit_code != 0 { - let out = output_lines( - Some(output), - OutputLinesParams { - only_err: false, - include_angle_pipe: false, - include_prefix: false, - }, - ) - .into_iter() - .join("\n"); + let params = OutputLinesParams { + only_err: false, + include_angle_pipe: false, + include_prefix: false, + }; + let out = output_lines(Some(output), ¶ms).into_iter().join("\n"); if !out.trim().is_empty() { // Wrap the output. for line in out.lines() { @@ -519,7 +512,13 @@ impl ExecCell { } } } - lines.extend(prefix_lines(body_lines, " └ ".dim(), " ".into())); + let initial_prefix = " └ ".dim(); + let subsequent_prefix = " ".into(); + lines.extend(prefix_lines( + body_lines, + &initial_prefix, + &subsequent_prefix, + )); lines } } @@ -1114,7 +1113,7 @@ pub(crate) fn new_limits_unavailable() -> PlainHistoryCell { } #[allow(clippy::disallowed_methods)] -pub(crate) fn new_warning_event(message: String) -> PlainHistoryCell { +pub(crate) fn new_warning_event(message: &str) -> PlainHistoryCell { PlainHistoryCell { lines: vec![vec![format!("⚠ {message}").yellow()].into()], } @@ -1309,7 +1308,7 @@ pub(crate) fn empty_mcp_output() -> PlainHistoryCell { /// Render MCP tools grouped by connection using the fully-qualified tool names. pub(crate) fn new_mcp_tools_output( config: &Config, - tools: std::collections::HashMap, + tools: &std::collections::HashMap, ) -> PlainHistoryCell { let mut lines: Vec> = vec![ "/mcp".magenta().into(), @@ -1362,7 +1361,7 @@ pub(crate) fn new_info_event(message: String, hint: Option) -> PlainHist PlainHistoryCell { lines } } -pub(crate) fn new_error_event(message: String) -> PlainHistoryCell { +pub(crate) fn new_error_event(message: &str) -> PlainHistoryCell { // Use a hair space (U+200A) to create a subtle, near-invisible separation // before the text. VS16 is intentionally omitted to keep spacing tighter // in terminals like Ghostty. @@ -1412,7 +1411,9 @@ impl HistoryCell for PlanUpdateCell { .into_iter() .map(|s| s.to_string().set_style(step_style).into()) .collect(); - prefix_lines(step_text, box_str.into(), " ".into()) + let box_span: Span<'static> = box_str.into(); + let indent_span: Span<'static> = " ".into(); + prefix_lines(step_text, &box_span, &indent_span) }; let mut lines: Vec> = vec![]; @@ -1435,7 +1436,13 @@ impl HistoryCell for PlanUpdateCell { indented_lines.extend(render_step(status, step)); } } - lines.extend(prefix_lines(indented_lines, " └ ".into(), " ".into())); + let initial_prefix: Span<'static> = " └ ".into(); + let subsequent_prefix: Span<'static> = " ".into(); + lines.extend(prefix_lines( + indented_lines, + &initial_prefix, + &subsequent_prefix, + )); lines } @@ -1463,6 +1470,11 @@ pub(crate) fn new_patch_apply_failure(stderr: String) -> PlainHistoryCell { lines.push(Line::from("✘ Failed to apply patch".magenta().bold())); if !stderr.trim().is_empty() { + let params = OutputLinesParams { + only_err: true, + include_angle_pipe: true, + include_prefix: true, + }; lines.extend(output_lines( Some(&CommandOutput { exit_code: 1, @@ -1470,11 +1482,7 @@ pub(crate) fn new_patch_apply_failure(stderr: String) -> PlainHistoryCell { stderr, formatted_output: String::new(), }), - OutputLinesParams { - only_err: true, - include_angle_pipe: true, - include_prefix: true, - }, + ¶ms, )); } @@ -1495,25 +1503,25 @@ pub(crate) fn new_proposed_command(command: &[String]) -> PlainHistoryCell { let subsequent_prefix: Span<'static> = " ".into(); lines.extend(prefix_lines( highlighted_lines, - initial_prefix, - subsequent_prefix, + &initial_prefix, + &subsequent_prefix, )); PlainHistoryCell { lines } } pub(crate) fn new_reasoning_block( - full_reasoning_buffer: String, + full_reasoning_buffer: &str, config: &Config, ) -> TranscriptOnlyHistoryCell { let mut lines: Vec> = Vec::new(); lines.push(Line::from("thinking".magenta().italic())); - append_markdown(&full_reasoning_buffer, &mut lines, config); + append_markdown(full_reasoning_buffer, &mut lines, config); TranscriptOnlyHistoryCell { lines } } pub(crate) fn new_reasoning_summary_block( - full_reasoning_buffer: String, + full_reasoning_buffer: &str, config: &Config, ) -> Box { if config.model_family.reasoning_summary_format == ReasoningSummaryFormat::Experimental { @@ -1523,19 +1531,19 @@ pub(crate) fn new_reasoning_summary_block( // reasoning summary // // So we need to strip header from reasoning summary - let full_reasoning_buffer = full_reasoning_buffer.trim(); - if let Some(open) = full_reasoning_buffer.find("**") { - let after_open = &full_reasoning_buffer[(open + 2)..]; + let trimmed = full_reasoning_buffer.trim(); + if let Some(open) = trimmed.find("**") { + let after_open = &trimmed[(open + 2)..]; if let Some(close) = after_open.find("**") { let after_close_idx = open + 2 + close + 2; // if we don't have anything beyond `after_close_idx` // then we don't have a summary to inject into history - if after_close_idx < full_reasoning_buffer.len() { - let header_buffer = full_reasoning_buffer[..after_close_idx].to_string(); + if after_close_idx < trimmed.len() { + let header_buffer = trimmed[..after_close_idx].to_string(); let mut header_lines = Vec::new(); append_markdown(&header_buffer, &mut header_lines, config); - let summary_buffer = full_reasoning_buffer[after_close_idx..].to_string(); + let summary_buffer = trimmed[after_close_idx..].to_string(); let mut summary_lines = Vec::new(); append_markdown(&summary_buffer, &mut summary_lines, config); @@ -1553,12 +1561,12 @@ struct OutputLinesParams { include_prefix: bool, } -fn output_lines(output: Option<&CommandOutput>, params: OutputLinesParams) -> Vec> { +fn output_lines(output: Option<&CommandOutput>, params: &OutputLinesParams) -> Vec> { let OutputLinesParams { only_err, include_angle_pipe, include_prefix, - } = params; + } = *params; let CommandOutput { exit_code, stdout, @@ -2173,7 +2181,7 @@ mod tests { config.model_family.reasoning_summary_format = ReasoningSummaryFormat::Experimental; let cell = new_reasoning_summary_block( - "**High level reasoning**\n\nDetailed reasoning goes here.".to_string(), + "**High level reasoning**\n\nDetailed reasoning goes here.", &config, ); @@ -2192,8 +2200,7 @@ mod tests { let mut config = test_config(); config.model_family.reasoning_summary_format = ReasoningSummaryFormat::Experimental; - let cell = - new_reasoning_summary_block("Detailed reasoning goes here.".to_string(), &config); + let cell = new_reasoning_summary_block("Detailed reasoning goes here.", &config); let rendered = render_transcript(cell.as_ref()); assert_eq!(rendered, vec!["thinking", "Detailed reasoning goes here."]); @@ -2204,10 +2211,7 @@ mod tests { let mut config = test_config(); config.model_family.reasoning_summary_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "**High level reasoning without closing".to_string(), - &config, - ); + let cell = new_reasoning_summary_block("**High level reasoning without closing", &config); let rendered = render_transcript(cell.as_ref()); assert_eq!( @@ -2221,10 +2225,7 @@ mod tests { let mut config = test_config(); config.model_family.reasoning_summary_format = ReasoningSummaryFormat::Experimental; - let cell = new_reasoning_summary_block( - "**High level reasoning without closing**".to_string(), - &config, - ); + let cell = new_reasoning_summary_block("**High level reasoning without closing**", &config); let rendered = render_transcript(cell.as_ref()); assert_eq!( @@ -2232,10 +2233,8 @@ mod tests { vec!["thinking", "High level reasoning without closing"] ); - let cell = new_reasoning_summary_block( - "**High level reasoning without closing**\n\n ".to_string(), - &config, - ); + let cell = + new_reasoning_summary_block("**High level reasoning without closing**\n\n ", &config); let rendered = render_transcript(cell.as_ref()); assert_eq!( @@ -2250,7 +2249,7 @@ mod tests { config.model_family.reasoning_summary_format = ReasoningSummaryFormat::Experimental; let cell = new_reasoning_summary_block( - "**High level plan**\n\nWe should fix the bug next.".to_string(), + "**High level plan**\n\nWe should fix the bug next.", &config, ); diff --git a/codex-rs/tui/src/insert_history.rs b/codex-rs/tui/src/insert_history.rs index 75731c7f55..777b0ec4ef 100644 --- a/codex-rs/tui/src/insert_history.rs +++ b/codex-rs/tui/src/insert_history.rs @@ -22,7 +22,7 @@ use ratatui::text::Span; /// Insert `lines` above the viewport using the terminal's backend writer /// (avoids direct stdout references). -pub(crate) fn insert_history_lines(terminal: &mut tui::Terminal, lines: Vec) { +pub(crate) fn insert_history_lines(terminal: &mut tui::Terminal, lines: &[Line]) { let mut out = std::io::stdout(); insert_history_lines_to_writer(terminal, &mut out, lines); } @@ -32,7 +32,7 @@ pub(crate) fn insert_history_lines(terminal: &mut tui::Terminal, lines: Vec( terminal: &mut crate::custom_terminal::Terminal, writer: &mut W, - lines: Vec, + lines: &[Line], ) where B: ratatui::backend::Backend, W: Write, @@ -43,7 +43,7 @@ pub fn insert_history_lines_to_writer( // Pre-wrap lines using word-aware wrapping so terminal scrollback sees the same // formatting as the TUI. This avoids character-level hard wrapping by the terminal. - let wrapped = word_wrap_lines_borrowed(&lines, area.width.max(1) as usize); + let wrapped = word_wrap_lines_borrowed(lines, area.width.max(1) as usize); let wrapped_lines = wrapped.len() as u16; let cursor_top = if area.bottom() < screen_size.height { // If the viewport is not at the bottom of the screen, scroll it down to make room. @@ -322,7 +322,8 @@ mod tests { let mut line: Line<'static> = Line::from(vec!["> ".into(), "Hello world".into()]); line = line.style(Color::Green); let mut ansi: Vec = Vec::new(); - insert_history_lines_to_writer(&mut term, &mut ansi, vec![line]); + let lines = vec![line]; + insert_history_lines_to_writer(&mut term, &mut ansi, &lines); // Parse ANSI using vt100 and assert at least one non-default fg color appears let mut parser = Parser::new(height, width, 0); @@ -365,7 +366,8 @@ mod tests { line = line.style(Color::Green); let mut ansi: Vec = Vec::new(); - insert_history_lines_to_writer(&mut term, &mut ansi, vec![line]); + let lines = vec![line]; + insert_history_lines_to_writer(&mut term, &mut ansi, &lines); // Parse and inspect the final screen buffer. let mut parser = Parser::new(height, width, 0); @@ -430,7 +432,8 @@ mod tests { ]); let mut ansi: Vec = Vec::new(); - insert_history_lines_to_writer(&mut term, &mut ansi, vec![line]); + let lines = vec![line]; + insert_history_lines_to_writer(&mut term, &mut ansi, &lines); let mut parser = Parser::new(height, width, 0); parser.process(&ansi); @@ -489,7 +492,7 @@ mod tests { term.set_viewport_area(viewport); let mut ansi: Vec = Vec::new(); - insert_history_lines_to_writer(&mut term, &mut ansi, lines); + insert_history_lines_to_writer(&mut term, &mut ansi, &lines); let mut parser = Parser::new(height, width, 0); parser.process(&ansi); diff --git a/codex-rs/tui/src/live_wrap.rs b/codex-rs/tui/src/live_wrap.rs index e78710dc6c..ef8aa495ea 100644 --- a/codex-rs/tui/src/live_wrap.rs +++ b/codex-rs/tui/src/live_wrap.rs @@ -170,14 +170,13 @@ impl RowBuilder { if suffix.is_empty() { // Fits entirely; keep in buffer (do not push yet) so we can append more later. break; - } else { - // Emit wrapped prefix as a non-explicit row and continue with the remainder. - self.rows.push(Row { - text: prefix, - explicit_break: false, - }); - self.current_line = suffix.to_string(); } + // Emit wrapped prefix as a non-explicit row and continue with the remainder. + self.rows.push(Row { + text: prefix, + explicit_break: false, + }); + self.current_line = suffix.to_string(); } } } diff --git a/codex-rs/tui/src/markdown_render.rs b/codex-rs/tui/src/markdown_render.rs index f20bfd75c0..8adbf528c8 100644 --- a/codex-rs/tui/src/markdown_render.rs +++ b/codex-rs/tui/src/markdown_render.rs @@ -52,7 +52,7 @@ pub(crate) fn render_markdown_text_with_citations( let parser = Parser::new_ext(input, options); let mut w = Writer::new( parser, - scheme.map(|s| s.to_string()), + scheme.map(std::string::ToString::to_string), Some(cwd.to_path_buf()), ); w.run(); @@ -108,7 +108,7 @@ where match event { Event::Start(tag) => self.start_tag(tag), Event::End(tag) => self.end_tag(tag), - Event::Text(text) => self.text(text), + Event::Text(text) => self.text(&text), Event::Code(code) => self.code(code), Event::SoftBreak => self.soft_break(), Event::HardBreak => self.hard_break(), @@ -119,8 +119,8 @@ where self.push_line(Line::from("———")); self.needs_newline = true; } - Event::Html(html) => self.html(html, false), - Event::InlineHtml(html) => self.html(html, true), + Event::Html(html) => self.html(&html, false), + Event::InlineHtml(html) => self.html(&html, true), Event::FootnoteReference(_) => {} Event::TaskListMarker(_) => {} } @@ -236,7 +236,7 @@ where self.needs_newline = true; } - fn text(&mut self, text: CowStr<'a>) { + fn text(&mut self, text: &CowStr<'a>) { if self.pending_marker_line { self.push_line(Line::default()); } @@ -287,7 +287,7 @@ where self.push_span(span); } - fn html(&mut self, html: CowStr<'a>, inline: bool) { + fn html(&mut self, html: &CowStr<'a>, inline: bool) { self.pending_marker_line = false; for (i, line) in html.lines().enumerate() { if self.needs_newline { @@ -329,7 +329,7 @@ where let is_ordered = self .list_indices .last() - .map(|index| index.is_some()) + .map(std::option::Option::is_some) .unwrap_or(false); let width = depth * 4 - 3; let marker = if let Some(last_index) = self.list_indices.last_mut() { diff --git a/codex-rs/tui/src/markdown_stream.rs b/codex-rs/tui/src/markdown_stream.rs index 12b4308210..a9428b891b 100644 --- a/codex-rs/tui/src/markdown_stream.rs +++ b/codex-rs/tui/src/markdown_stream.rs @@ -310,10 +310,8 @@ mod tests { let long = "> This is a very long quoted line that should wrap across multiple columns to verify style preservation."; let out = super::simulate_stream_markdown_for_tests(&[long, "\n"], true, &cfg); // Wrap to a narrow width to force multiple output lines. - let wrapped = crate::wrapping::word_wrap_lines( - out.iter().collect::>(), - crate::wrapping::RtOptions::new(24), - ); + let wrapped = + crate::wrapping::word_wrap_lines(out.iter(), crate::wrapping::RtOptions::new(24)); // Filter out purely blank lines let non_blank: Vec<_> = wrapped .into_iter() diff --git a/codex-rs/tui/src/new_model_popup.rs b/codex-rs/tui/src/new_model_popup.rs index 2106e46c67..f4266505de 100644 --- a/codex-rs/tui/src/new_model_popup.rs +++ b/codex-rs/tui/src/new_model_popup.rs @@ -98,7 +98,7 @@ impl WidgetRef for &ModelUpgradePopup { let mut lines: Vec = Vec::new(); if show_animation { let frame = self.animation.current_frame(); - lines.extend(frame.lines().map(|l| l.into())); + lines.extend(frame.lines().map(std::convert::Into::into)); // Spacer between animation and text content. lines.push("".into()); } diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs index 2ea59a312a..97e5e18484 100644 --- a/codex-rs/tui/src/onboarding/auth.rs +++ b/codex-rs/tui/src/onboarding/auth.rs @@ -118,7 +118,7 @@ impl KeyboardHandler for AuthModeWidget { } fn handle_paste(&mut self, pasted: String) { - let _ = self.handle_api_key_entry_paste(pasted); + let _ = self.handle_api_key_entry_paste(&pasted); } } @@ -403,7 +403,7 @@ impl AuthModeWidget { true } - fn handle_api_key_entry_paste(&mut self, pasted: String) -> bool { + fn handle_api_key_entry_paste(&mut self, pasted: &str) -> bool { let trimmed = pasted.trim(); if trimmed.is_empty() { return false; diff --git a/codex-rs/tui/src/onboarding/welcome.rs b/codex-rs/tui/src/onboarding/welcome.rs index c62b6402d8..2563246151 100644 --- a/codex-rs/tui/src/onboarding/welcome.rs +++ b/codex-rs/tui/src/onboarding/welcome.rs @@ -62,7 +62,7 @@ impl WidgetRef for &WelcomeWidget { let frame = self.animation.current_frame(); // let frame_line_count = frame.lines().count(); // lines.reserve(frame_line_count + 2); - lines.extend(frame.lines().map(|l| l.into())); + lines.extend(frame.lines().map(std::convert::Into::into)); lines.push("".into()); } lines.push(Line::from(vec![ diff --git a/codex-rs/tui/src/pager_overlay.rs b/codex-rs/tui/src/pager_overlay.rs index e5524b252b..6882ce0bef 100644 --- a/codex-rs/tui/src/pager_overlay.rs +++ b/codex-rs/tui/src/pager_overlay.rs @@ -35,7 +35,7 @@ impl Overlay { Self::Static(StaticOverlay::with_title(lines, title)) } - pub(crate) fn handle_event(&mut self, tui: &mut tui::Tui, event: TuiEvent) -> Result<()> { + pub(crate) fn handle_event(&mut self, tui: &mut tui::Tui, event: &TuiEvent) -> Result<()> { match self { Overlay::Transcript(o) => o.handle_event(tui, event), Overlay::Static(o) => o.handle_event(tui, event), @@ -382,7 +382,7 @@ impl TranscriptOverlay { let cell_lines = if Some(idx) == highlight_cell { cell.transcript_lines() .into_iter() - .map(|l| l.reversed()) + .map(ratatui::prelude::Stylize::reversed) .collect() } else { cell.transcript_lines() @@ -440,7 +440,7 @@ impl TranscriptOverlay { } impl TranscriptOverlay { - pub(crate) fn handle_event(&mut self, tui: &mut tui::Tui, event: TuiEvent) -> Result<()> { + pub(crate) fn handle_event(&mut self, tui: &mut tui::Tui, event: &TuiEvent) -> Result<()> { match event { TuiEvent::Key(key_event) => match key_event { KeyEvent { @@ -463,7 +463,7 @@ impl TranscriptOverlay { self.is_done = true; Ok(()) } - other => self.view.handle_key_event(tui, other), + other => self.view.handle_key_event(tui, *other), }, TuiEvent::Draw => { tui.draw(u16::MAX, |frame| { @@ -510,7 +510,7 @@ impl StaticOverlay { } impl StaticOverlay { - pub(crate) fn handle_event(&mut self, tui: &mut tui::Tui, event: TuiEvent) -> Result<()> { + pub(crate) fn handle_event(&mut self, tui: &mut tui::Tui, event: &TuiEvent) -> Result<()> { match event { TuiEvent::Key(key_event) => match key_event { KeyEvent { @@ -527,7 +527,7 @@ impl StaticOverlay { self.is_done = true; Ok(()) } - other => self.view.handle_key_event(tui, other), + other => self.view.handle_key_event(tui, *other), }, TuiEvent::Draw => { tui.draw(u16::MAX, |frame| { diff --git a/codex-rs/tui/src/render/line_utils.rs b/codex-rs/tui/src/render/line_utils.rs index 5a8e09068a..723ea2bcdc 100644 --- a/codex-rs/tui/src/render/line_utils.rs +++ b/codex-rs/tui/src/render/line_utils.rs @@ -39,8 +39,8 @@ pub fn is_blank_line_spaces_only(line: &Line<'_>) -> bool { /// `subsequent_prefix` for following lines. Returns a new Vec of owned lines. pub fn prefix_lines( lines: Vec>, - initial_prefix: Span<'static>, - subsequent_prefix: Span<'static>, + initial_prefix: &Span<'static>, + subsequent_prefix: &Span<'static>, ) -> Vec> { lines .into_iter() diff --git a/codex-rs/tui/src/session_log.rs b/codex-rs/tui/src/session_log.rs index 3118800b71..6bb3e843a7 100644 --- a/codex-rs/tui/src/session_log.rs +++ b/codex-rs/tui/src/session_log.rs @@ -45,7 +45,7 @@ impl SessionLogger { Ok(()) } - fn write_json_line(&self, value: serde_json::Value) { + fn write_json_line(&self, value: &serde_json::Value) { let Some(mutex) = self.file.get() else { return; }; @@ -53,7 +53,7 @@ impl SessionLogger { Ok(g) => g, Err(poisoned) => poisoned.into_inner(), }; - match serde_json::to_string(&value) { + match serde_json::to_string(value) { Ok(serialized) => { if let Err(e) = guard.write_all(serialized.as_bytes()) { tracing::warn!("session log write error: {}", e); @@ -119,7 +119,7 @@ pub(crate) fn maybe_init(config: &Config) { "model_provider_id": config.model_provider_id, "model_provider_name": config.model_provider.name, }); - LOGGER.write_json_line(header); + LOGGER.write_json_line(&header); } pub(crate) fn log_inbound_app_event(event: &AppEvent) { @@ -138,7 +138,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) { "dir": "to_tui", "kind": "new_session", }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } AppEvent::InsertHistoryCell(cell) => { let value = json!({ @@ -147,7 +147,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) { "kind": "insert_history_cell", "lines": cell.transcript_lines().len(), }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } AppEvent::StartFileSearch(query) => { let value = json!({ @@ -156,7 +156,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) { "kind": "file_search_start", "query": query, }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } AppEvent::FileSearchResult { query, matches } => { let value = json!({ @@ -166,7 +166,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) { "query": query, "matches": matches.len(), }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } // Noise or control flow – record variant only other => { @@ -176,7 +176,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) { "kind": "app_event", "variant": format!("{other:?}").split('(').next().unwrap_or("app_event"), }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } } } @@ -197,7 +197,7 @@ pub(crate) fn log_session_end() { "dir": "meta", "kind": "session_end", }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } fn write_record(dir: &str, kind: &str, obj: &T) @@ -210,5 +210,5 @@ where "kind": kind, "payload": obj, }); - LOGGER.write_json_line(value); + LOGGER.write_json_line(&value); } diff --git a/codex-rs/tui/src/tui.rs b/codex-rs/tui/src/tui.rs index eeabec4e04..3f22ae4c7e 100644 --- a/codex-rs/tui/src/tui.rs +++ b/codex-rs/tui/src/tui.rs @@ -416,10 +416,10 @@ impl Tui { } #[cfg(unix)] - fn apply_prepared_resume_action(&mut self, prepared: PreparedResumeAction) -> Result<()> { + fn apply_prepared_resume_action(&mut self, prepared: &PreparedResumeAction) -> Result<()> { match prepared { PreparedResumeAction::RealignViewport(area) => { - self.terminal.set_viewport_area(area); + self.terminal.set_viewport_area(*area); } PreparedResumeAction::RestoreAltScreen => { execute!(self.terminal.backend_mut(), EnterAlternateScreen)?; @@ -511,7 +511,7 @@ impl Tui { #[cfg(unix)] { if let Some(prepared) = prepared_resume.take() { - self.apply_prepared_resume_action(prepared)?; + self.apply_prepared_resume_action(&prepared)?; } } let terminal = &mut self.terminal; @@ -536,10 +536,7 @@ impl Tui { terminal.set_viewport_area(area); } if !self.pending_history_lines.is_empty() { - crate::insert_history::insert_history_lines( - terminal, - self.pending_history_lines.clone(), - ); + crate::insert_history::insert_history_lines(terminal, &self.pending_history_lines); self.pending_history_lines.clear(); } // Update the y position for suspending so Ctrl-Z can place the cursor correctly. diff --git a/codex-rs/tui/src/user_approval_widget.rs b/codex-rs/tui/src/user_approval_widget.rs index 410766e0fe..777837522a 100644 --- a/codex-rs/tui/src/user_approval_widget.rs +++ b/codex-rs/tui/src/user_approval_widget.rs @@ -219,10 +219,10 @@ impl UserApprovalWidget { } fn send_decision(&mut self, decision: ReviewDecision) { - self.send_decision_with_feedback(decision, String::new()) + self.send_decision_with_feedback(decision, "") } - fn send_decision_with_feedback(&mut self, decision: ReviewDecision, feedback: String) { + fn send_decision_with_feedback(&mut self, decision: ReviewDecision, feedback: &str) { match &self.approval_request { ApprovalRequest::Exec { command, .. } => { let full_cmd = strip_bash_lc_and_escape(command); diff --git a/codex-rs/tui/src/wrapping.rs b/codex-rs/tui/src/wrapping.rs index c97f0e3c6c..a9d3cd6d4a 100644 --- a/codex-rs/tui/src/wrapping.rs +++ b/codex-rs/tui/src/wrapping.rs @@ -511,7 +511,7 @@ mod tests { .subsequent_indent(Line::from(" ")); let lines = [Line::from("hello world"), Line::from("foo bar baz")]; - let out = word_wrap_lines_borrowed(lines.iter().collect::>(), opts); + let out = word_wrap_lines_borrowed(lines.iter(), opts); let rendered: Vec = out.iter().map(concat_line).collect(); assert!(rendered.first().unwrap().starts_with("- ")); @@ -523,7 +523,7 @@ mod tests { #[test] fn wrap_lines_borrowed_without_indents_is_concat_of_single_wraps() { let lines = [Line::from("hello"), Line::from("world!")]; - let out = word_wrap_lines_borrowed(lines.iter().collect::>(), 10); + let out = word_wrap_lines_borrowed(lines.iter(), 10); let rendered: Vec = out.iter().map(concat_line).collect(); assert_eq!(rendered, vec!["hello", "world!"]); } @@ -543,7 +543,10 @@ mod tests { let lines = [line]; // Force small width to exercise wrapping at spaces. let wrapped = word_wrap_lines_borrowed(&lines, 40); - let joined: String = wrapped.iter().map(|l| l.to_string()).join("\n"); + let joined: String = wrapped + .iter() + .map(std::string::ToString::to_string) + .join("\n"); assert_eq!( joined, r#"Years passed, and Willowmere thrived diff --git a/codex-rs/tui/tests/suite/vt100_history.rs b/codex-rs/tui/tests/suite/vt100_history.rs index 973b0aa052..9d89e9c34b 100644 --- a/codex-rs/tui/tests/suite/vt100_history.rs +++ b/codex-rs/tui/tests/suite/vt100_history.rs @@ -41,7 +41,7 @@ impl TestScenario { } } - fn run_insert(&mut self, lines: Vec>) -> Vec { + fn run_insert(&mut self, lines: &[Line<'static>]) -> Vec { let mut buf: Vec = Vec::new(); codex_tui::insert_history::insert_history_lines_to_writer(&mut self.term, &mut buf, lines); buf @@ -79,7 +79,7 @@ fn basic_insertion_no_wrap() { let mut scenario = TestScenario::new(20, 6, area); let lines = vec!["first".into(), "second".into()]; - let buf = scenario.run_insert(lines); + let buf = scenario.run_insert(&lines); let rows = scenario.screen_rows_from_bytes(&buf); assert_contains!(rows, String::from("first")); assert_contains!(rows, String::from("second")); @@ -101,7 +101,7 @@ fn long_token_wraps() { let long = "A".repeat(45); // > 2 lines at width 20 let lines = vec![long.clone().into()]; - let buf = scenario.run_insert(lines); + let buf = scenario.run_insert(&lines); let mut parser = vt100::Parser::new(6, 20, 0); parser.process(&buf); let screen = parser.screen(); @@ -133,7 +133,7 @@ fn emoji_and_cjk() { let text = String::from("😀😀😀😀😀 你好世界"); let lines = vec![text.clone().into()]; - let buf = scenario.run_insert(lines); + let buf = scenario.run_insert(&lines); let rows = scenario.screen_rows_from_bytes(&buf); let reconstructed: String = rows.join("").chars().filter(|c| *c != ' ').collect(); for ch in text.chars().filter(|c| !c.is_whitespace()) { @@ -150,7 +150,8 @@ fn mixed_ansi_spans() { let mut scenario = TestScenario::new(20, 6, area); let line = vec!["red".red(), "+plain".into()].into(); - let buf = scenario.run_insert(vec![line]); + let lines = vec![line]; + let buf = scenario.run_insert(&lines); let rows = scenario.screen_rows_from_bytes(&buf); assert_contains!(rows, String::from("red+plain")); } @@ -161,7 +162,7 @@ fn cursor_restoration() { let mut scenario = TestScenario::new(20, 6, area); let lines = vec!["x".into()]; - let buf = scenario.run_insert(lines); + let buf = scenario.run_insert(&lines); let s = String::from_utf8_lossy(&buf); // CUP to 1;1 (ANSI: ESC[1;1H) assert!( @@ -182,7 +183,8 @@ fn word_wrap_no_mid_word_split() { let mut scenario = TestScenario::new(40, 10, area); let sample = "Years passed, and Willowmere thrived in peace and friendship. Mira’s herb garden flourished with both ordinary and enchanted plants, and travelers spoke of the kindness of the woman who tended them."; - let buf = scenario.run_insert(vec![sample.into()]); + let lines = vec![sample.into()]; + let buf = scenario.run_insert(&lines); let rows = scenario.screen_rows_from_bytes(&buf); let joined = rows.join("\n"); assert!( @@ -198,7 +200,8 @@ fn em_dash_and_space_word_wrap() { let mut scenario = TestScenario::new(40, 10, area); let sample = "Mara found an old key on the shore. Curious, she opened a tarnished box half-buried in sand—and inside lay a single, glowing seed."; - let buf = scenario.run_insert(vec![sample.into()]); + let lines = vec![sample.into()]; + let buf = scenario.run_insert(&lines); let rows = scenario.screen_rows_from_bytes(&buf); let joined = rows.join("\n"); assert!( @@ -214,7 +217,7 @@ fn pre_scroll_region_down() { let mut scenario = TestScenario::new(20, 6, area); let lines = vec!["first".into(), "second".into()]; - let buf = scenario.run_insert(lines); + let buf = scenario.run_insert(&lines); let s = String::from_utf8_lossy(&buf); // Expect we limited scroll region to [top+1 .. screen_height] => [4 .. 6] (1-based) assert!( diff --git a/codex-rs/tui/tests/suite/vt100_live_commit.rs b/codex-rs/tui/tests/suite/vt100_live_commit.rs index e06f10275c..83947c1976 100644 --- a/codex-rs/tui/tests/suite/vt100_live_commit.rs +++ b/codex-rs/tui/tests/suite/vt100_live_commit.rs @@ -27,7 +27,7 @@ fn live_001_commit_on_overflow() { let lines: Vec> = commit_rows.into_iter().map(|r| r.text.into()).collect(); let mut buf: Vec = Vec::new(); - codex_tui::insert_history::insert_history_lines_to_writer(&mut term, &mut buf, lines); + codex_tui::insert_history::insert_history_lines_to_writer(&mut term, &mut buf, &lines); let mut parser = vt100::Parser::new(6, 20, 0); parser.process(&buf); @@ -80,7 +80,7 @@ fn live_002_pre_scroll_and_commit() { let lines: Vec> = commit_rows.into_iter().map(|r| r.text.into()).collect(); let mut buf: Vec = Vec::new(); - codex_tui::insert_history::insert_history_lines_to_writer(&mut term, &mut buf, lines); + codex_tui::insert_history::insert_history_lines_to_writer(&mut term, &mut buf, &lines); let s = String::from_utf8_lossy(&buf); // Expect a SetScrollRegion to [area.top()+1 .. screen_height] and a cursor move to top of that region. diff --git a/codex-rs/tui/tests/suite/vt100_streaming_no_dup.rs b/codex-rs/tui/tests/suite/vt100_streaming_no_dup.rs index 79f9d2fb00..306dee5ba6 100644 --- a/codex-rs/tui/tests/suite/vt100_streaming_no_dup.rs +++ b/codex-rs/tui/tests/suite/vt100_streaming_no_dup.rs @@ -19,19 +19,13 @@ fn stream_commit_trickle_no_duplication() { // Step 1: commit first row let mut out1 = Vec::new(); - codex_tui::insert_history::insert_history_lines_to_writer( - &mut t, - &mut out1, - vec!["one".into()], - ); + let lines1 = vec!["one".into()]; + codex_tui::insert_history::insert_history_lines_to_writer(&mut t, &mut out1, &lines1); // Step 2: later commit next row let mut out2 = Vec::new(); - codex_tui::insert_history::insert_history_lines_to_writer( - &mut t, - &mut out2, - vec!["two".into()], - ); + let lines2 = vec!["two".into()]; + codex_tui::insert_history::insert_history_lines_to_writer(&mut t, &mut out2, &lines2); let combined = [out1, out2].concat(); let s = String::from_utf8_lossy(&combined); @@ -58,11 +52,8 @@ fn live_ring_rows_not_inserted_into_history() { // Commit two rows to history. let mut buf = Vec::new(); - codex_tui::insert_history::insert_history_lines_to_writer( - &mut t, - &mut buf, - vec!["one".into(), "two".into()], - ); + let lines = vec!["one".into(), "two".into()]; + codex_tui::insert_history::insert_history_lines_to_writer(&mut t, &mut buf, &lines); // The live ring might display tail+head rows like ["two", "three"], // but only committed rows should be present in the history ANSI stream.