Files
codex/prs/bolinfest/PR-2465.md
2025-09-02 15:17:45 -07:00

1651 lines
72 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PR #2465: chore: upgrade to Rust 1.89
- URL: https://github.com/openai/codex/pull/2465
- Author: bolinfest
- Created: 2025-08-19 19:01:29 UTC
- Updated: 2025-08-19 20:22:10 UTC
- Changes: +496/-513, Files changed: 37, Commits: 1
## Description
Codex created this PR from the following prompt:
> upgrade this entire repo to Rust 1.89. Note that this requires updating codex-rs/rust-toolchain.toml as well as the workflows in .github/. Make sure that things are "clippy clean" as this change will likely uncover new Clippy errors. `just fmt` and `cargo clippy --tests` are sufficient to check for correctness
Note this modifies a lot of lines because it folds nested `if` statements using `&&`.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/2465).
* #2467
* __->__ #2465
## Full Diff
```diff
diff --git a/.github/workflows/codex.yml b/.github/workflows/codex.yml
index cb31275534..367c53e947 100644
--- a/.github/workflows/codex.yml
+++ b/.github/workflows/codex.yml
@@ -39,7 +39,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v5
- - uses: dtolnay/rust-toolchain@1.88
+ - uses: dtolnay/rust-toolchain@1.89
with:
targets: x86_64-unknown-linux-gnu
components: clippy
diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml
index fca1a02288..fa9f1cd15f 100644
--- a/.github/workflows/rust-ci.yml
+++ b/.github/workflows/rust-ci.yml
@@ -57,7 +57,7 @@ jobs:
working-directory: codex-rs
steps:
- uses: actions/checkout@v5
- - uses: dtolnay/rust-toolchain@1.88
+ - uses: dtolnay/rust-toolchain@1.89
with:
components: rustfmt
- name: cargo fmt
@@ -112,7 +112,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- - uses: dtolnay/rust-toolchain@1.88
+ - uses: dtolnay/rust-toolchain@1.89
with:
targets: ${{ matrix.target }}
components: clippy
diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml
index c00a7a5e03..0044b864c7 100644
--- a/.github/workflows/rust-release.yml
+++ b/.github/workflows/rust-release.yml
@@ -75,7 +75,7 @@ jobs:
steps:
- uses: actions/checkout@v5
- - uses: dtolnay/rust-toolchain@1.88
+ - uses: dtolnay/rust-toolchain@1.89
with:
targets: ${{ matrix.target }}
diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs
index 87df66dd23..1aae106682 100644
--- a/codex-rs/apply-patch/src/lib.rs
+++ b/codex-rs/apply-patch/src/lib.rs
@@ -415,12 +415,12 @@ fn apply_hunks_to_files(hunks: &[Hunk]) -> anyhow::Result<AffectedPaths> {
for hunk in hunks {
match hunk {
Hunk::AddFile { path, contents } => {
- if let Some(parent) = path.parent() {
- if !parent.as_os_str().is_empty() {
- std::fs::create_dir_all(parent).with_context(|| {
- format!("Failed to create parent directories for {}", path.display())
- })?;
- }
+ if let Some(parent) = path.parent()
+ && !parent.as_os_str().is_empty()
+ {
+ std::fs::create_dir_all(parent).with_context(|| {
+ format!("Failed to create parent directories for {}", path.display())
+ })?;
}
std::fs::write(path, contents)
.with_context(|| format!("Failed to write file {}", path.display()))?;
@@ -439,15 +439,12 @@ fn apply_hunks_to_files(hunks: &[Hunk]) -> anyhow::Result<AffectedPaths> {
let AppliedPatch { new_contents, .. } =
derive_new_contents_from_chunks(path, chunks)?;
if let Some(dest) = move_path {
- if let Some(parent) = dest.parent() {
- if !parent.as_os_str().is_empty() {
- std::fs::create_dir_all(parent).with_context(|| {
- format!(
- "Failed to create parent directories for {}",
- dest.display()
- )
- })?;
- }
+ if let Some(parent) = dest.parent()
+ && !parent.as_os_str().is_empty()
+ {
+ std::fs::create_dir_all(parent).with_context(|| {
+ format!("Failed to create parent directories for {}", dest.display())
+ })?;
}
std::fs::write(dest, new_contents)
.with_context(|| format!("Failed to write file {}", dest.display()))?;
@@ -529,9 +526,12 @@ fn compute_replacements(
// If a chunk has a `change_context`, we use seek_sequence to find it, then
// adjust our `line_index` to continue from there.
if let Some(ctx_line) = &chunk.change_context {
- if let Some(idx) =
- seek_sequence::seek_sequence(original_lines, &[ctx_line.clone()], line_index, false)
- {
+ if let Some(idx) = seek_sequence::seek_sequence(
+ original_lines,
+ std::slice::from_ref(ctx_line),
+ line_index,
+ false,
+ ) {
line_index = idx + 1;
} else {
return Err(ApplyPatchError::ComputeReplacements(format!(
diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs
index b7766fe70a..216a0437d1 100644
--- a/codex-rs/arg0/src/lib.rs
+++ b/codex-rs/arg0/src/lib.rs
@@ -89,10 +89,10 @@ const ILLEGAL_ENV_VAR_PREFIX: &str = "CODEX_";
/// Security: Do not allow `.env` files to create or modify any variables
/// with names starting with `CODEX_`.
fn load_dotenv() {
- if let Ok(codex_home) = codex_core::config::find_codex_home() {
- if let Ok(iter) = dotenvy::from_path_iter(codex_home.join(".env")) {
- set_filtered(iter);
- }
+ if let Ok(codex_home) = codex_core::config::find_codex_home()
+ && let Ok(iter) = dotenvy::from_path_iter(codex_home.join(".env"))
+ {
+ set_filtered(iter);
}
if let Ok(iter) = dotenvy::dotenv_iter() {
diff --git a/codex-rs/cli/src/login.rs b/codex-rs/cli/src/login.rs
index 72eb7b4faf..959bf46f18 100644
--- a/codex-rs/cli/src/login.rs
+++ b/codex-rs/cli/src/login.rs
@@ -66,12 +66,12 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
Ok(api_key) => {
eprintln!("Logged in using an API key - {}", safe_format_key(&api_key));
- if let Ok(env_api_key) = env::var(OPENAI_API_KEY_ENV_VAR) {
- if env_api_key == api_key {
- eprintln!(
- " API loaded from OPENAI_API_KEY environment variable or .env file"
- );
- }
+ if let Ok(env_api_key) = env::var(OPENAI_API_KEY_ENV_VAR)
+ && env_api_key == api_key
+ {
+ eprintln!(
+ " API loaded from OPENAI_API_KEY environment variable or .env file"
+ );
}
std::process::exit(0);
}
diff --git a/codex-rs/core/src/chat_completions.rs b/codex-rs/core/src/chat_completions.rs
index fc0ceca5ad..f3ed34c6dc 100644
--- a/codex-rs/core/src/chat_completions.rs
+++ b/codex-rs/core/src/chat_completions.rs
@@ -290,13 +290,12 @@ async fn process_chat_sse<S>(
.get("delta")
.and_then(|d| d.get("content"))
.and_then(|c| c.as_str())
+ && !content.is_empty()
{
- if !content.is_empty() {
- assistant_text.push_str(content);
- let _ = tx_event
- .send(Ok(ResponseEvent::OutputTextDelta(content.to_string())))
- .await;
- }
+ assistant_text.push_str(content);
+ let _ = tx_event
+ .send(Ok(ResponseEvent::OutputTextDelta(content.to_string())))
+ .await;
}
// Forward any reasoning/thinking deltas if present.
@@ -333,27 +332,25 @@ async fn process_chat_sse<S>(
.get("delta")
.and_then(|d| d.get("tool_calls"))
.and_then(|tc| tc.as_array())
+ && let Some(tool_call) = tool_calls.first()
{
- if let Some(tool_call) = tool_calls.first() {
- // Mark that we have an active function call in progress.
- fn_call_state.active = true;
+ // Mark that we have an active function call in progress.
+ fn_call_state.active = true;
- // Extract call_id if present.
- if let Some(id) = tool_call.get("id").and_then(|v| v.as_str()) {
- fn_call_state.call_id.get_or_insert_with(|| id.to_string());
- }
+ // Extract call_id if present.
+ if let Some(id) = tool_call.get("id").and_then(|v| v.as_str()) {
+ fn_call_state.call_id.get_or_insert_with(|| id.to_string());
+ }
- // Extract function details if present.
- if let Some(function) = tool_call.get("function") {
- if let Some(name) = function.get("name").and_then(|n| n.as_str()) {
- fn_call_state.name.get_or_insert_with(|| name.to_string());
- }
+ // Extract function details if present.
+ if let Some(function) = tool_call.get("function") {
+ if let Some(name) = function.get("name").and_then(|n| n.as_str()) {
+ fn_call_state.name.get_or_insert_with(|| name.to_string());
+ }
- if let Some(args_fragment) =
- function.get("arguments").and_then(|a| a.as_str())
- {
- fn_call_state.arguments.push_str(args_fragment);
- }
+ if let Some(args_fragment) = function.get("arguments").and_then(|a| a.as_str())
+ {
+ fn_call_state.arguments.push_str(args_fragment);
}
}
}
@@ -491,15 +488,14 @@ where
// Only use the final assistant message if we have not
// seen any deltas; otherwise, deltas already built the
// cumulative text and this would duplicate it.
- if this.cumulative.is_empty() {
- if let crate::models::ResponseItem::Message { content, .. } = &item {
- if let Some(text) = content.iter().find_map(|c| match c {
- crate::models::ContentItem::OutputText { text } => Some(text),
- _ => None,
- }) {
- this.cumulative.push_str(text);
- }
- }
+ if this.cumulative.is_empty()
+ && let crate::models::ResponseItem::Message { content, .. } = &item
+ && let Some(text) = content.iter().find_map(|c| match c {
+ crate::models::ContentItem::OutputText { text } => Some(text),
+ _ => None,
+ })
+ {
+ this.cumulative.push_str(text);
}
// Swallow assistant message here; emit on Completed.
diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs
index 6423fb44ef..57719f5269 100644
--- a/codex-rs/core/src/codex.rs
+++ b/codex-rs/core/src/codex.rs
@@ -544,10 +544,10 @@ impl Session {
pub fn remove_task(&self, sub_id: &str) {
let mut state = self.state.lock_unchecked();
- if let Some(task) = &state.current_task {
- if task.sub_id == sub_id {
- state.current_task.take();
- }
+ if let Some(task) = &state.current_task
+ && task.sub_id == sub_id
+ {
+ state.current_task.take();
}
}
@@ -1239,18 +1239,18 @@ async fn submission_loop(
// Gracefully flush and shutdown rollout recorder on session end so tests
// that inspect the rollout file do not race with the background writer.
let recorder_opt = sess.rollout.lock_unchecked().take();
- if let Some(rec) = recorder_opt {
- if let Err(e) = rec.shutdown().await {
- warn!("failed to shutdown rollout recorder: {e}");
- let event = Event {
- id: sub.id.clone(),
- msg: EventMsg::Error(ErrorEvent {
- message: "Failed to shutdown rollout recorder".to_string(),
- }),
- };
- if let Err(e) = sess.tx_event.send(event).await {
- warn!("failed to send error message: {e:?}");
- }
+ if let Some(rec) = recorder_opt
+ && let Err(e) = rec.shutdown().await
+ {
+ warn!("failed to shutdown rollout recorder: {e}");
+ let event = Event {
+ id: sub.id.clone(),
+ msg: EventMsg::Error(ErrorEvent {
+ message: "Failed to shutdown rollout recorder".to_string(),
+ }),
+ };
+ if let Err(e) = sess.tx_event.send(event).await {
+ warn!("failed to send error message: {e:?}");
}
}
diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs
index 760729d827..e0ff411f15 100644
--- a/codex-rs/core/src/config.rs
+++ b/codex-rs/core/src/config.rs
@@ -759,10 +759,10 @@ fn default_model() -> String {
pub fn find_codex_home() -> std::io::Result<PathBuf> {
// Honor the `CODEX_HOME` environment variable when it is set to allow users
// (and tests) to override the default location.
- if let Ok(val) = std::env::var("CODEX_HOME") {
- if !val.is_empty() {
- return PathBuf::from(val).canonicalize();
- }
+ if let Ok(val) = std::env::var("CODEX_HOME")
+ && !val.is_empty()
+ {
+ return PathBuf::from(val).canonicalize();
}
let mut p = home_dir().ok_or_else(|| {
diff --git a/codex-rs/core/src/git_info.rs b/codex-rs/core/src/git_info.rs
index 09a59b7b8c..ccb43ae55a 100644
--- a/codex-rs/core/src/git_info.rs
+++ b/codex-rs/core/src/git_info.rs
@@ -51,33 +51,30 @@ pub async fn collect_git_info(cwd: &Path) -> Option<GitInfo> {
};
// Process commit hash
- if let Some(output) = commit_result {
- if output.status.success() {
- if let Ok(hash) = String::from_utf8(output.stdout) {
- git_info.commit_hash = Some(hash.trim().to_string());
- }
- }
+ if let Some(output) = commit_result
+ && output.status.success()
+ && let Ok(hash) = String::from_utf8(output.stdout)
+ {
+ git_info.commit_hash = Some(hash.trim().to_string());
}
// Process branch name
- if let Some(output) = branch_result {
- if output.status.success() {
- if let Ok(branch) = String::from_utf8(output.stdout) {
- let branch = branch.trim();
- if branch != "HEAD" {
- git_info.branch = Some(branch.to_string());
- }
- }
+ if let Some(output) = branch_result
+ && output.status.success()
+ && let Ok(branch) = String::from_utf8(output.stdout)
+ {
+ let branch = branch.trim();
+ if branch != "HEAD" {
+ git_info.branch = Some(branch.to_string());
}
}
// Process repository URL
- if let Some(output) = url_result {
- if output.status.success() {
- if let Ok(url) = String::from_utf8(output.stdout) {
- git_info.repository_url = Some(url.trim().to_string());
- }
- }
+ if let Some(output) = url_result
+ && output.status.success()
+ && let Ok(url) = String::from_utf8(output.stdout)
+ {
+ git_info.repository_url = Some(url.trim().to_string());
}
Some(git_info)
diff --git a/codex-rs/core/src/is_safe_command.rs b/codex-rs/core/src/is_safe_command.rs
index 38dd9562f8..f54e247fd8 100644
--- a/codex-rs/core/src/is_safe_command.rs
+++ b/codex-rs/core/src/is_safe_command.rs
@@ -12,20 +12,17 @@ pub fn is_known_safe_command(command: &[String]) -> bool {
// introduce side effects ( "&&", "||", ";", and "|" ). If every
// individual command in the script is itself a knownsafe command, then
// the composite expression is considered safe.
- if let [bash, flag, script] = command {
- if bash == "bash" && flag == "-lc" {
- if let Some(tree) = try_parse_bash(script) {
- if let Some(all_commands) = try_parse_word_only_commands_sequence(&tree, script) {
- if !all_commands.is_empty()
- && all_commands
- .iter()
- .all(|cmd| is_safe_to_call_with_exec(cmd))
- {
- return true;
- }
- }
- }
- }
+ if let [bash, flag, script] = command
+ && bash == "bash"
+ && flag == "-lc"
+ && let Some(tree) = try_parse_bash(script)
+ && let Some(all_commands) = try_parse_word_only_commands_sequence(&tree, script)
+ && !all_commands.is_empty()
+ && all_commands
+ .iter()
+ .all(|cmd| is_safe_to_call_with_exec(cmd))
+ {
+ return true;
}
false
diff --git a/codex-rs/core/src/model_provider_info.rs b/codex-rs/core/src/model_provider_info.rs
index 00eabc539c..0102591d2c 100644
--- a/codex-rs/core/src/model_provider_info.rs
+++ b/codex-rs/core/src/model_provider_info.rs
@@ -167,10 +167,10 @@ impl ModelProviderInfo {
if let Some(env_headers) = &self.env_http_headers {
for (header, env_var) in env_headers {
- if let Ok(val) = std::env::var(env_var) {
- if !val.trim().is_empty() {
- builder = builder.header(header, val);
- }
+ if let Ok(val) = std::env::var(env_var)
+ && !val.trim().is_empty()
+ {
+ builder = builder.header(header, val);
}
}
}
diff --git a/codex-rs/core/src/openai_tools.rs b/codex-rs/core/src/openai_tools.rs
index 32ead20e7d..dd5eb12516 100644
--- a/codex-rs/core/src/openai_tools.rs
+++ b/codex-rs/core/src/openai_tools.rs
@@ -420,11 +420,11 @@ fn sanitize_json_schema(value: &mut JsonValue) {
}
JsonValue::Object(map) => {
// First, recursively sanitize known nested schema holders
- if let Some(props) = map.get_mut("properties") {
- if let Some(props_map) = props.as_object_mut() {
- for (_k, v) in props_map.iter_mut() {
- sanitize_json_schema(v);
- }
+ if let Some(props) = map.get_mut("properties")
+ && let Some(props_map) = props.as_object_mut()
+ {
+ for (_k, v) in props_map.iter_mut() {
+ sanitize_json_schema(v);
}
}
if let Some(items) = map.get_mut("items") {
@@ -444,18 +444,18 @@ fn sanitize_json_schema(value: &mut JsonValue) {
.map(|s| s.to_string());
// If type is an array (union), pick first supported; else leave to inference
- if ty.is_none() {
- if let Some(JsonValue::Array(types)) = map.get("type") {
- for t in types {
- if let Some(tt) = t.as_str() {
- if matches!(
- tt,
- "object" | "array" | "string" | "number" | "integer" | "boolean"
- ) {
- ty = Some(tt.to_string());
- break;
- }
- }
+ if ty.is_none()
+ && let Some(JsonValue::Array(types)) = map.get("type")
+ {
+ for t in types {
+ if let Some(tt) = t.as_str()
+ && matches!(
+ tt,
+ "object" | "array" | "string" | "number" | "integer" | "boolean"
+ )
+ {
+ ty = Some(tt.to_string());
+ break;
}
}
}
diff --git a/codex-rs/core/src/parse_command.rs b/codex-rs/core/src/parse_command.rs
index 6ea0626814..7348af43ac 100644
--- a/codex-rs/core/src/parse_command.rs
+++ b/codex-rs/core/src/parse_command.rs
@@ -1196,10 +1196,10 @@ fn simplify_once(commands: &[ParsedCommand]) -> Option<Vec<ParsedCommand>> {
}
// echo ... && ...rest => ...rest
- if let ParsedCommand::Unknown { cmd } = &commands[0] {
- if shlex_split(cmd).is_some_and(|t| t.first().map(|s| s.as_str()) == Some("echo")) {
- return Some(commands[1..].to_vec());
- }
+ if let ParsedCommand::Unknown { cmd } = &commands[0]
+ && shlex_split(cmd).is_some_and(|t| t.first().map(|s| s.as_str()) == Some("echo"))
+ {
+ return Some(commands[1..].to_vec());
}
// cd foo && [any Test command] => [any Test command]
@@ -1208,17 +1208,15 @@ fn simplify_once(commands: &[ParsedCommand]) -> Option<Vec<ParsedCommand>> {
shlex_split(cmd).is_some_and(|t| t.first().map(|s| s.as_str()) == Some("cd"))
}
_ => false,
- }) {
- if commands
- .iter()
- .skip(idx + 1)
- .any(|pc| matches!(pc, ParsedCommand::Test { .. }))
- {
- let mut out = Vec::with_capacity(commands.len() - 1);
- out.extend_from_slice(&commands[..idx]);
- out.extend_from_slice(&commands[idx + 1..]);
- return Some(out);
- }
+ }) && commands
+ .iter()
+ .skip(idx + 1)
+ .any(|pc| matches!(pc, ParsedCommand::Test { .. }))
+ {
+ let mut out = Vec::with_capacity(commands.len() - 1);
+ out.extend_from_slice(&commands[..idx]);
+ out.extend_from_slice(&commands[idx + 1..]);
+ return Some(out);
}
// cmd || true => cmd
@@ -1564,127 +1562,124 @@ fn parse_bash_lc_commands(original: &[String]) -> Option<Vec<ParsedCommand>> {
if bash != "bash" || flag != "-lc" {
return None;
}
- if let Some(tree) = try_parse_bash(script) {
- if let Some(all_commands) = try_parse_word_only_commands_sequence(&tree, script) {
- if !all_commands.is_empty() {
- let script_tokens = shlex_split(script)
- .unwrap_or_else(|| vec!["bash".to_string(), flag.clone(), script.clone()]);
- // Strip small formatting helpers (e.g., head/tail/awk/wc/etc) so we
- // bias toward the primary command when pipelines are present.
- // First, drop obvious small formatting helpers (e.g., wc/awk/etc).
- let had_multiple_commands = all_commands.len() > 1;
- // The bash AST walker yields commands in right-to-left order for
- // connector/pipeline sequences. Reverse to reflect actual execution order.
- let mut filtered_commands = drop_small_formatting_commands(all_commands);
- filtered_commands.reverse();
- if filtered_commands.is_empty() {
- return Some(vec![ParsedCommand::Unknown {
- cmd: script.clone(),
- }]);
- }
- let mut commands: Vec<ParsedCommand> = filtered_commands
- .into_iter()
- .map(|tokens| summarize_main_tokens(&tokens))
- .collect();
- if commands.len() > 1 {
- commands.retain(|pc| !matches!(pc, ParsedCommand::Noop { .. }));
- }
- if commands.len() == 1 {
- // If we reduced to a single command, attribute the full original script
- // for clearer UX in file-reading and listing scenarios, or when there were
- // no connectors in the original script. For search commands that came from
- // a pipeline (e.g. `rg --files | sed -n`), keep only the primary command.
- let had_connectors = had_multiple_commands
- || script_tokens
- .iter()
- .any(|t| t == "|" || t == "&&" || t == "||" || t == ";");
- commands = commands
- .into_iter()
- .map(|pc| match pc {
- ParsedCommand::Read { name, cmd, .. } => {
- 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")
- });
- if has_pipe && has_sed_n {
- ParsedCommand::Read {
- cmd: script.clone(),
- name,
- }
- } else {
- ParsedCommand::Read {
- cmd: cmd.clone(),
- name,
- }
- }
- } else {
- ParsedCommand::Read {
- cmd: shlex_join(&script_tokens),
- name,
- }
+ if let Some(tree) = try_parse_bash(script)
+ && let Some(all_commands) = try_parse_word_only_commands_sequence(&tree, script)
+ && !all_commands.is_empty()
+ {
+ let script_tokens = shlex_split(script)
+ .unwrap_or_else(|| vec!["bash".to_string(), flag.clone(), script.clone()]);
+ // Strip small formatting helpers (e.g., head/tail/awk/wc/etc) so we
+ // bias toward the primary command when pipelines are present.
+ // First, drop obvious small formatting helpers (e.g., wc/awk/etc).
+ let had_multiple_commands = all_commands.len() > 1;
+ // The bash AST walker yields commands in right-to-left order for
+ // connector/pipeline sequences. Reverse to reflect actual execution order.
+ let mut filtered_commands = drop_small_formatting_commands(all_commands);
+ filtered_commands.reverse();
+ if filtered_commands.is_empty() {
+ return Some(vec![ParsedCommand::Unknown {
+ cmd: script.clone(),
+ }]);
+ }
+ let mut commands: Vec<ParsedCommand> = filtered_commands
+ .into_iter()
+ .map(|tokens| summarize_main_tokens(&tokens))
+ .collect();
+ if commands.len() > 1 {
+ commands.retain(|pc| !matches!(pc, ParsedCommand::Noop { .. }));
+ }
+ if commands.len() == 1 {
+ // If we reduced to a single command, attribute the full original script
+ // for clearer UX in file-reading and listing scenarios, or when there were
+ // no connectors in the original script. For search commands that came from
+ // a pipeline (e.g. `rg --files | sed -n`), keep only the primary command.
+ let had_connectors = had_multiple_commands
+ || script_tokens
+ .iter()
+ .any(|t| t == "|" || t == "&&" || t == "||" || t == ";");
+ commands = commands
+ .into_iter()
+ .map(|pc| match pc {
+ ParsedCommand::Read { name, cmd, .. } => {
+ 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")
+ });
+ if has_pipe && has_sed_n {
+ ParsedCommand::Read {
+ cmd: script.clone(),
+ name,
}
- }
- ParsedCommand::ListFiles { path, cmd, .. } => {
- if had_connectors {
- ParsedCommand::ListFiles {
- cmd: cmd.clone(),
- path,
- }
- } else {
- ParsedCommand::ListFiles {
- cmd: shlex_join(&script_tokens),
- path,
- }
+ } else {
+ ParsedCommand::Read {
+ cmd: cmd.clone(),
+ name,
}
}
- ParsedCommand::Search {
- query, path, cmd, ..
- } => {
- if had_connectors {
- ParsedCommand::Search {
- cmd: cmd.clone(),
- query,
- path,
- }
- } else {
- ParsedCommand::Search {
- cmd: shlex_join(&script_tokens),
- query,
- path,
- }
- }
+ } else {
+ ParsedCommand::Read {
+ cmd: shlex_join(&script_tokens),
+ name,
}
- ParsedCommand::Format {
- tool, targets, cmd, ..
- } => ParsedCommand::Format {
+ }
+ }
+ ParsedCommand::ListFiles { path, cmd, .. } => {
+ if had_connectors {
+ ParsedCommand::ListFiles {
cmd: cmd.clone(),
- tool,
- targets,
- },
- ParsedCommand::Test { cmd, .. } => {
- ParsedCommand::Test { cmd: cmd.clone() }
+ path,
}
- ParsedCommand::Lint {
- tool, targets, cmd, ..
- } => ParsedCommand::Lint {
+ } else {
+ ParsedCommand::ListFiles {
+ cmd: shlex_join(&script_tokens),
+ path,
+ }
+ }
+ }
+ ParsedCommand::Search {
+ query, path, cmd, ..
+ } => {
+ if had_connectors {
+ ParsedCommand::Search {
cmd: cmd.clone(),
- tool,
- targets,
- },
- ParsedCommand::Unknown { .. } => ParsedCommand::Unknown {
- cmd: script.clone(),
- },
- ParsedCommand::Noop { .. } => ParsedCommand::Noop {
- cmd: script.clone(),
- },
- })
- .collect();
- }
- return Some(commands);
- }
+ query,
+ path,
+ }
+ } else {
+ ParsedCommand::Search {
+ cmd: shlex_join(&script_tokens),
+ query,
+ path,
+ }
+ }
+ }
+ ParsedCommand::Format {
+ tool, targets, cmd, ..
+ } => ParsedCommand::Format {
+ cmd: cmd.clone(),
+ tool,
+ targets,
+ },
+ ParsedCommand::Test { cmd, .. } => ParsedCommand::Test { cmd: cmd.clone() },
+ ParsedCommand::Lint {
+ tool, targets, cmd, ..
+ } => ParsedCommand::Lint {
+ cmd: cmd.clone(),
+ tool,
+ targets,
+ },
+ ParsedCommand::Unknown { .. } => ParsedCommand::Unknown {
+ cmd: script.clone(),
+ },
+ ParsedCommand::Noop { .. } => ParsedCommand::Noop {
+ cmd: script.clone(),
+ },
+ })
+ .collect();
}
+ return Some(commands);
}
Some(vec![ParsedCommand::Unknown {
cmd: script.clone(),
diff --git a/codex-rs/core/src/safety.rs b/codex-rs/core/src/safety.rs
index c878a71110..37cb8f4e4c 100644
--- a/codex-rs/core/src/safety.rs
+++ b/codex-rs/core/src/safety.rs
@@ -231,10 +231,10 @@ fn is_write_patch_constrained_to_writable_paths(
if !is_path_writable(path) {
return false;
}
- if let Some(dest) = move_path {
- if !is_path_writable(dest) {
- return false;
- }
+ if let Some(dest) = move_path
+ && !is_path_writable(dest)
+ {
+ return false;
}
}
}
diff --git a/codex-rs/core/src/shell.rs b/codex-rs/core/src/shell.rs
index 0cde3b4a78..c269b87ef2 100644
--- a/codex-rs/core/src/shell.rs
+++ b/codex-rs/core/src/shell.rs
@@ -70,13 +70,13 @@ pub async fn default_user_shell() -> Shell {
}
let stdout = String::from_utf8_lossy(&o.stdout);
for line in stdout.lines() {
- if let Some(shell_path) = line.strip_prefix("UserShell: ") {
- if shell_path.ends_with("/zsh") {
- return Shell::Zsh(ZshShell {
- shell_path: shell_path.to_string(),
- zshrc_path: format!("{home}/.zshrc"),
- });
- }
+ if let Some(shell_path) = line.strip_prefix("UserShell: ")
+ && shell_path.ends_with("/zsh")
+ {
+ return Shell::Zsh(ZshShell {
+ shell_path: shell_path.to_string(),
+ zshrc_path: format!("{home}/.zshrc"),
+ });
}
}
diff --git a/codex-rs/core/tests/cli_stream.rs b/codex-rs/core/tests/cli_stream.rs
index 219e99787d..dd53a8d302 100644
--- a/codex-rs/core/tests/cli_stream.rs
+++ b/codex-rs/core/tests/cli_stream.rs
@@ -297,13 +297,12 @@ async fn integration_creates_and_checks_session_file() {
Ok(v) => v,
Err(_) => continue,
};
- if item.get("type").and_then(|t| t.as_str()) == Some("message") {
- if let Some(c) = item.get("content") {
- if c.to_string().contains(&marker) {
- matching_path = Some(path.to_path_buf());
- break;
- }
- }
+ if item.get("type").and_then(|t| t.as_str()) == Some("message")
+ && let Some(c) = item.get("content")
+ && c.to_string().contains(&marker)
+ {
+ matching_path = Some(path.to_path_buf());
+ break;
}
}
}
@@ -376,13 +375,12 @@ async fn integration_creates_and_checks_session_file() {
let Ok(item) = serde_json::from_str::<serde_json::Value>(line) else {
continue;
};
- if item.get("type").and_then(|t| t.as_str()) == Some("message") {
- if let Some(c) = item.get("content") {
- if c.to_string().contains(&marker) {
- found_message = true;
- break;
- }
- }
+ if item.get("type").and_then(|t| t.as_str()) == Some("message")
+ && let Some(c) = item.get("content")
+ && c.to_string().contains(&marker)
+ {
+ found_message = true;
+ break;
}
}
assert!(
diff --git a/codex-rs/exec/src/event_processor.rs b/codex-rs/exec/src/event_processor.rs
index b7b3c27dc5..1ba10c34e2 100644
--- a/codex-rs/exec/src/event_processor.rs
+++ b/codex-rs/exec/src/event_processor.rs
@@ -29,9 +29,9 @@ pub(crate) fn handle_last_message(last_agent_message: Option<&str>, output_file:
}
fn write_last_message_file(contents: &str, last_message_path: Option<&Path>) {
- if let Some(path) = last_message_path {
- if let Err(e) = std::fs::write(path, contents) {
- eprintln!("Failed to write last message file {path:?}: {e}");
- }
+ if let Some(path) = last_message_path
+ && let Err(e) = std::fs::write(path, contents)
+ {
+ eprintln!("Failed to write last message file {path:?}: {e}");
}
}
diff --git a/codex-rs/execpolicy/src/execv_checker.rs b/codex-rs/execpolicy/src/execv_checker.rs
index 242ea6d177..fcd80b2b5f 100644
--- a/codex-rs/execpolicy/src/execv_checker.rs
+++ b/codex-rs/execpolicy/src/execv_checker.rs
@@ -214,7 +214,12 @@ system_path=[{fake_cp:?}]
// Only readable folders specified.
assert_eq!(
- checker.check(valid_exec.clone(), &cwd, &[root_path.clone()], &[]),
+ checker.check(
+ valid_exec.clone(),
+ &cwd,
+ std::slice::from_ref(&root_path),
+ &[]
+ ),
Err(WriteablePathNotInWriteableFolders {
file: dest_path.clone(),
folders: vec![]
@@ -226,8 +231,8 @@ system_path=[{fake_cp:?}]
checker.check(
valid_exec.clone(),
&cwd,
- &[root_path.clone()],
- &[root_path.clone()]
+ std::slice::from_ref(&root_path),
+ std::slice::from_ref(&root_path)
),
Ok(cp.clone()),
);
@@ -246,8 +251,8 @@ system_path=[{fake_cp:?}]
checker.check(
valid_exec_call_folders_as_args,
&cwd,
- &[root_path.clone()],
- &[root_path.clone()]
+ std::slice::from_ref(&root_path),
+ std::slice::from_ref(&root_path)
),
Ok(cp.clone()),
);
@@ -269,8 +274,8 @@ system_path=[{fake_cp:?}]
checker.check(
exec_with_parent_of_readable_folder,
&cwd,
- &[root_path.clone()],
- &[dest_path.clone()]
+ std::slice::from_ref(&root_path),
+ std::slice::from_ref(&dest_path)
),
Err(ReadablePathNotInReadableFolders {
file: root_path.parent().unwrap().to_path_buf(),
diff --git a/codex-rs/execpolicy/src/policy.rs b/codex-rs/execpolicy/src/policy.rs
index 7ce148b7c2..825d6164a5 100644
--- a/codex-rs/execpolicy/src/policy.rs
+++ b/codex-rs/execpolicy/src/policy.rs
@@ -56,16 +56,16 @@ impl Policy {
}
for arg in args {
- if let Some(regex) = &self.forbidden_substrings_pattern {
- if regex.is_match(arg) {
- return Ok(MatchedExec::Forbidden {
- cause: Forbidden::Arg {
- arg: arg.clone(),
- exec_call: exec_call.clone(),
- },
- reason: format!("arg `{arg}` contains forbidden substring"),
- });
- }
+ if let Some(regex) = &self.forbidden_substrings_pattern
+ && regex.is_match(arg)
+ {
+ return Ok(MatchedExec::Forbidden {
+ cause: Forbidden::Arg {
+ arg: arg.clone(),
+ exec_call: exec_call.clone(),
+ },
+ reason: format!("arg `{arg}` contains forbidden substring"),
+ });
}
}
diff --git a/codex-rs/execpolicy/src/sed_command.rs b/codex-rs/execpolicy/src/sed_command.rs
index 64494ddf00..cc96aa98e7 100644
--- a/codex-rs/execpolicy/src/sed_command.rs
+++ b/codex-rs/execpolicy/src/sed_command.rs
@@ -3,12 +3,12 @@ use crate::error::Result;
pub fn parse_sed_command(sed_command: &str) -> Result<()> {
// For now, we parse only commands like `122,202p`.
- if let Some(stripped) = sed_command.strip_suffix("p") {
- if let Some((first, rest)) = stripped.split_once(",") {
- if first.parse::<u64>().is_ok() && rest.parse::<u64>().is_ok() {
- return Ok(());
- }
- }
+ if let Some(stripped) = sed_command.strip_suffix("p")
+ && let Some((first, rest)) = stripped.split_once(",")
+ && first.parse::<u64>().is_ok()
+ && rest.parse::<u64>().is_ok()
+ {
+ return Ok(());
}
Err(Error::SedCommandNotProvablySafe {
diff --git a/codex-rs/file-search/src/lib.rs b/codex-rs/file-search/src/lib.rs
index e0eb87656b..bfcfe922ef 100644
--- a/codex-rs/file-search/src/lib.rs
+++ b/codex-rs/file-search/src/lib.rs
@@ -228,11 +228,11 @@ pub fn run(
for &Reverse((score, ref line)) in best_list.binary_heap.iter() {
if global_heap.len() < limit.get() {
global_heap.push(Reverse((score, line.clone())));
- } else if let Some(min_element) = global_heap.peek() {
- if score > min_element.0.0 {
- global_heap.pop();
- global_heap.push(Reverse((score, line.clone())));
- }
+ } else if let Some(min_element) = global_heap.peek()
+ && score > min_element.0.0
+ {
+ global_heap.pop();
+ global_heap.push(Reverse((score, line.clone())));
}
}
}
@@ -320,11 +320,11 @@ impl BestMatchesList {
if self.binary_heap.len() < self.max_count {
self.binary_heap.push(Reverse((score, line.to_string())));
- } else if let Some(min_element) = self.binary_heap.peek() {
- if score > min_element.0.0 {
- self.binary_heap.pop();
- self.binary_heap.push(Reverse((score, line.to_string())));
- }
+ } else if let Some(min_element) = self.binary_heap.peek()
+ && score > min_element.0.0
+ {
+ self.binary_heap.pop();
+ self.binary_heap.push(Reverse((score, line.to_string())));
}
}
}
diff --git a/codex-rs/login/src/server.rs b/codex-rs/login/src/server.rs
index 32229484ff..6ffc86219d 100644
--- a/codex-rs/login/src/server.rs
+++ b/codex-rs/login/src/server.rs
@@ -364,10 +364,10 @@ async fn persist_tokens_async(
let codex_home = codex_home.to_path_buf();
tokio::task::spawn_blocking(move || {
let auth_file = get_auth_file(&codex_home);
- if let Some(parent) = auth_file.parent() {
- if !parent.exists() {
- std::fs::create_dir_all(parent).map_err(io::Error::other)?;
- }
+ if let Some(parent) = auth_file.parent()
+ && !parent.exists()
+ {
+ std::fs::create_dir_all(parent).map_err(io::Error::other)?;
}
let mut auth = read_or_default(&auth_file);
diff --git a/codex-rs/ollama/src/client.rs b/codex-rs/ollama/src/client.rs
index 55c86e31f8..45e0486156 100644
--- a/codex-rs/ollama/src/client.rs
+++ b/codex-rs/ollama/src/client.rs
@@ -166,9 +166,8 @@ impl OllamaClient {
yield PullEvent::Error(err_msg.to_string());
return;
}
- if let Some(status) = value.get("status").and_then(|s| s.as_str()) {
- if status == "success" { yield PullEvent::Success; return; }
- }
+ if let Some(status) = value.get("status").and_then(|s| s.as_str())
+ && status == "success" { yield PullEvent::Success; return; }
}
}
}
diff --git a/codex-rs/protocol-ts/src/lib.rs b/codex-rs/protocol-ts/src/lib.rs
index a37130b83b..c2b196dada 100644
--- a/codex-rs/protocol-ts/src/lib.rs
+++ b/codex-rs/protocol-ts/src/lib.rs
@@ -48,18 +48,16 @@ pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
}
// Format with Prettier by passing individual files (no shell globbing)
- if let Some(prettier_bin) = prettier {
- if !ts_files.is_empty() {
- let status = Command::new(prettier_bin)
- .arg("--write")
- .args(ts_files.iter().map(|p| p.as_os_str()))
- .status()
- .with_context(|| {
- format!("Failed to invoke Prettier at {}", prettier_bin.display())
- })?;
- if !status.success() {
- return Err(anyhow!("Prettier failed with status {}", status));
- }
+ if let Some(prettier_bin) = prettier
+ && !ts_files.is_empty()
+ {
+ let status = Command::new(prettier_bin)
+ .arg("--write")
+ .args(ts_files.iter().map(|p| p.as_os_str()))
+ .status()
+ .with_context(|| format!("Failed to invoke Prettier at {}", prettier_bin.display()))?;
+ if !status.success() {
+ return Err(anyhow!("Prettier failed with status {}", status));
}
}
diff --git a/codex-rs/rust-toolchain.toml b/codex-rs/rust-toolchain.toml
index 72bafdf4b6..0a9f6e8169 100644
--- a/codex-rs/rust-toolchain.toml
+++ b/codex-rs/rust-toolchain.toml
@@ -1,3 +1,3 @@
[toolchain]
-channel = "1.88.0"
+channel = "1.89.0"
components = [ "clippy", "rustfmt", "rust-src"]
diff --git a/codex-rs/tui/src/bottom_pane/approval_modal_view.rs b/codex-rs/tui/src/bottom_pane/approval_modal_view.rs
index 0cfc24d780..b7e6e5e69a 100644
--- a/codex-rs/tui/src/bottom_pane/approval_modal_view.rs
+++ b/codex-rs/tui/src/bottom_pane/approval_modal_view.rs
@@ -33,10 +33,10 @@ impl ApprovalModalView<'_> {
/// Advance to next request if the current one is finished.
fn maybe_advance(&mut self) {
- if self.current.is_complete() {
- if let Some(req) = self.queue.pop() {
- self.current = UserApprovalWidget::new(req, self.app_event_tx.clone());
- }
+ if self.current.is_complete()
+ && let Some(req) = self.queue.pop()
+ {
+ self.current = UserApprovalWidget::new(req, self.app_event_tx.clone());
}
}
}
diff --git a/codex-rs/tui/src/bottom_pane/textarea.rs b/codex-rs/tui/src/bottom_pane/textarea.rs
index d2a3916e55..8379deb9f8 100644
--- a/codex-rs/tui/src/bottom_pane/textarea.rs
+++ b/codex-rs/tui/src/bottom_pane/textarea.rs
@@ -1575,53 +1575,53 @@ mod tests {
}
14 => {
// Try inserting inside an existing element (should clamp to boundary)
- if let Some(payload) = elem_texts.choose(&mut rng).cloned() {
- if let Some(start) = ta.text().find(&payload) {
- let end = start + payload.len();
- if end - start > 2 {
- let pos = rng.random_range(start + 1..end - 1);
- let ins = rand_grapheme(&mut rng);
- ta.insert_str_at(pos, &ins);
- }
+ if let Some(payload) = elem_texts.choose(&mut rng).cloned()
+ && let Some(start) = ta.text().find(&payload)
+ {
+ let end = start + payload.len();
+ if end - start > 2 {
+ let pos = rng.random_range(start + 1..end - 1);
+ let ins = rand_grapheme(&mut rng);
+ ta.insert_str_at(pos, &ins);
}
}
}
15 => {
// Replace a range that intersects an element -> whole element should be replaced
- if let Some(payload) = elem_texts.choose(&mut rng).cloned() {
- if let Some(start) = ta.text().find(&payload) {
- let end = start + payload.len();
- // Create an intersecting range [start-δ, end-δ2)
- let mut s = start.saturating_sub(rng.random_range(0..=2));
- let mut e = (end + rng.random_range(0..=2)).min(ta.text().len());
- // Align to char boundaries to satisfy String::replace_range contract
- let txt = ta.text();
- while s > 0 && !txt.is_char_boundary(s) {
- s -= 1;
- }
- while e < txt.len() && !txt.is_char_boundary(e) {
- e += 1;
- }
- if s < e {
- // Small replacement text
- let mut srep = String::new();
- for _ in 0..rng.random_range(0..=2) {
- srep.push_str(&rand_grapheme(&mut rng));
- }
- ta.replace_range(s..e, &srep);
+ if let Some(payload) = elem_texts.choose(&mut rng).cloned()
+ && let Some(start) = ta.text().find(&payload)
+ {
+ let end = start + payload.len();
+ // Create an intersecting range [start-δ, end-δ2)
+ let mut s = start.saturating_sub(rng.random_range(0..=2));
+ let mut e = (end + rng.random_range(0..=2)).min(ta.text().len());
+ // Align to char boundaries to satisfy String::replace_range contract
+ let txt = ta.text();
+ while s > 0 && !txt.is_char_boundary(s) {
+ s -= 1;
+ }
+ while e < txt.len() && !txt.is_char_boundary(e) {
+ e += 1;
+ }
+ if s < e {
+ // Small replacement text
+ let mut srep = String::new();
+ for _ in 0..rng.random_range(0..=2) {
+ srep.push_str(&rand_grapheme(&mut rng));
}
+ ta.replace_range(s..e, &srep);
}
}
}
16 => {
// Try setting the cursor to a position inside an element; it should clamp out
- if let Some(payload) = elem_texts.choose(&mut rng).cloned() {
- if let Some(start) = ta.text().find(&payload) {
- let end = start + payload.len();
- if end - start > 2 {
- let pos = rng.random_range(start + 1..end - 1);
- ta.set_cursor(pos);
- }
+ if let Some(payload) = elem_texts.choose(&mut rng).cloned()
+ && let Some(start) = ta.text().find(&payload)
+ {
+ let end = start + payload.len();
+ if end - start > 2 {
+ let pos = rng.random_range(start + 1..end - 1);
+ ta.set_cursor(pos);
}
}
}
diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs
index 192ab3c6ae..c7e9ad5b03 100644
--- a/codex-rs/tui/src/chatwidget/tests.rs
+++ b/codex-rs/tui/src/chatwidget/tests.rs
@@ -339,18 +339,18 @@ async fn binary_size_transcript_matches_ideal_fixture() {
}
}
"app_event" => {
- if let Some(variant) = v.get("variant").and_then(|s| s.as_str()) {
- if variant == "CommitTick" {
- chat.on_commit_tick();
- while let Ok(app_ev) = rx.try_recv() {
- if let AppEvent::InsertHistory(lines) = app_ev {
- transcript.push_str(&lines_to_single_string(&lines));
- crate::insert_history::insert_history_lines_to_writer(
- &mut terminal,
- &mut ansi,
- lines,
- );
- }
+ if let Some(variant) = v.get("variant").and_then(|s| s.as_str())
+ && variant == "CommitTick"
+ {
+ chat.on_commit_tick();
+ while let Ok(app_ev) = rx.try_recv() {
+ if let AppEvent::InsertHistory(lines) = app_ev {
+ transcript.push_str(&lines_to_single_string(&lines));
+ crate::insert_history::insert_history_lines_to_writer(
+ &mut terminal,
+ &mut ansi,
+ lines,
+ );
}
}
}
diff --git a/codex-rs/tui/src/custom_terminal.rs b/codex-rs/tui/src/custom_terminal.rs
index 1ada679fc1..91153f92ed 100644
--- a/codex-rs/tui/src/custom_terminal.rs
+++ b/codex-rs/tui/src/custom_terminal.rs
@@ -264,10 +264,10 @@ where
#[allow(clippy::print_stderr)]
fn drop(&mut self) {
// Attempt to restore the cursor state
- if self.hidden_cursor {
- if let Err(err) = self.show_cursor() {
- eprintln!("Failed to show the cursor: {err}");
- }
+ if self.hidden_cursor
+ && let Err(err) = self.show_cursor()
+ {
+ eprintln!("Failed to show the cursor: {err}");
}
}
}
@@ -309,7 +309,7 @@ where
}
/// Get a Frame object which provides a consistent view into the terminal state for rendering.
- pub fn get_frame(&mut self) -> Frame {
+ pub fn get_frame(&mut self) -> Frame<'_> {
let count = self.frame_count;
Frame {
cursor_position: None,
diff --git a/codex-rs/tui/src/exec_command.rs b/codex-rs/tui/src/exec_command.rs
index 35c59b22a5..db3bf2fef4 100644
--- a/codex-rs/tui/src/exec_command.rs
+++ b/codex-rs/tui/src/exec_command.rs
@@ -33,10 +33,10 @@ where
return None;
}
- if let Some(home_dir) = std::env::var_os("HOME").map(PathBuf::from) {
- if let Ok(rel) = path.strip_prefix(&home_dir) {
- return Some(rel.to_path_buf());
- }
+ if let Some(home_dir) = std::env::var_os("HOME").map(PathBuf::from)
+ && let Ok(rel) = path.strip_prefix(&home_dir)
+ {
+ return Some(rel.to_path_buf());
}
None
diff --git a/codex-rs/tui/src/file_search.rs b/codex-rs/tui/src/file_search.rs
index 8a05b2c165..7f15e46abb 100644
--- a/codex-rs/tui/src/file_search.rs
+++ b/codex-rs/tui/src/file_search.rs
@@ -94,13 +94,13 @@ impl FileSearchManager {
// If there is an in-flight search that is definitely obsolete,
// cancel it now.
- if let Some(active_search) = &st.active_search {
- if !query.starts_with(&active_search.query) {
- active_search
- .cancellation_token
- .store(true, Ordering::Relaxed);
- st.active_search = None;
- }
+ if let Some(active_search) = &st.active_search
+ && !query.starts_with(&active_search.query)
+ {
+ active_search
+ .cancellation_token
+ .store(true, Ordering::Relaxed);
+ st.active_search = None;
}
// Schedule a search to run after debounce.
@@ -187,10 +187,10 @@ impl FileSearchManager {
{
#[expect(clippy::unwrap_used)]
let mut st = search_state.lock().unwrap();
- if let Some(active_search) = &st.active_search {
- if Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token) {
- st.active_search = None;
- }
+ if let Some(active_search) = &st.active_search
+ && Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token)
+ {
+ st.active_search = None;
}
}
});
diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs
index 58677c724a..021c09e337 100644
--- a/codex-rs/tui/src/history_cell.rs
+++ b/codex-rs/tui/src/history_cell.rs
@@ -541,33 +541,33 @@ pub(crate) fn new_status_output(
// 👤 Account (only if ChatGPT tokens exist), shown under the first block
let auth_file = get_auth_file(&config.codex_home);
- if let Ok(auth) = try_read_auth_json(&auth_file) {
- if let Some(tokens) = auth.tokens.clone() {
- lines.push(Line::from(vec!["👤 ".into(), "Account".bold()]));
- lines.push(Line::from(" • Signed in with ChatGPT"));
-
- let info = tokens.id_token;
- if let Some(email) = &info.email {
- lines.push(Line::from(vec![" • Login: ".into(), email.clone().into()]));
- }
+ if let Ok(auth) = try_read_auth_json(&auth_file)
+ && let Some(tokens) = auth.tokens.clone()
+ {
+ lines.push(Line::from(vec!["👤 ".into(), "Account".bold()]));
+ lines.push(Line::from(" • Signed in with ChatGPT"));
- match auth.openai_api_key.as_deref() {
- Some(key) if !key.is_empty() => {
- lines.push(Line::from(
- " • Using API key. Run codex login to use ChatGPT plan",
- ));
- }
- _ => {
- let plan_text = info
- .get_chatgpt_plan_type()
- .map(|s| title_case(&s))
- .unwrap_or_else(|| "Unknown".to_string());
- lines.push(Line::from(vec![" • Plan: ".into(), plan_text.into()]));
- }
- }
+ let info = tokens.id_token;
+ if let Some(email) = &info.email {
+ lines.push(Line::from(vec![" • Login: ".into(), email.clone().into()]));
+ }
- lines.push(Line::from(""));
+ match auth.openai_api_key.as_deref() {
+ Some(key) if !key.is_empty() => {
+ lines.push(Line::from(
+ " • Using API key. Run codex login to use ChatGPT plan",
+ ));
+ }
+ _ => {
+ let plan_text = info
+ .get_chatgpt_plan_type()
+ .map(|s| title_case(&s))
+ .unwrap_or_else(|| "Unknown".to_string());
+ lines.push(Line::from(vec![" • Plan: ".into(), plan_text.into()]));
+ }
}
+
+ lines.push(Line::from(""));
}
// 🧠 Model
@@ -612,10 +612,10 @@ pub(crate) fn new_status_output(
" • Input: ".into(),
usage.non_cached_input().to_string().into(),
];
- if let Some(cached) = usage.cached_input_tokens {
- if cached > 0 {
- input_line_spans.push(format!(" (+ {cached} cached)").into());
- }
+ if let Some(cached) = usage.cached_input_tokens
+ && cached > 0
+ {
+ input_line_spans.push(format!(" (+ {cached} cached)").into());
}
lines.push(Line::from(input_line_spans));
// Output: <output>
@@ -688,16 +688,15 @@ pub(crate) fn new_mcp_tools_output(
]));
}
- if let Some(env) = cfg.env.as_ref() {
- if !env.is_empty() {
- let mut env_pairs: Vec<String> =
- env.iter().map(|(k, v)| format!("{k}={v}")).collect();
- env_pairs.sort();
- lines.push(Line::from(vec![
- " • Env: ".into(),
- env_pairs.join(" ").into(),
- ]));
- }
+ if let Some(env) = cfg.env.as_ref()
+ && !env.is_empty()
+ {
+ let mut env_pairs: Vec<String> = env.iter().map(|(k, v)| format!("{k}={v}")).collect();
+ env_pairs.sort();
+ lines.push(Line::from(vec![
+ " • Env: ".into(),
+ env_pairs.join(" ").into(),
+ ]));
}
if names.is_empty() {
diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs
index 2396b4d5fa..2ef70ca275 100644
--- a/codex-rs/tui/src/onboarding/auth.rs
+++ b/codex-rs/tui/src/onboarding/auth.rs
@@ -123,20 +123,20 @@ impl AuthModeWidget {
// If the user is already authenticated but the method differs from their
// preferred auth method, show a brief explanation.
- if let LoginStatus::AuthMode(current) = self.login_status {
- if current != self.preferred_auth_method {
- let to_label = |mode: AuthMode| match mode {
- AuthMode::ApiKey => "API key",
- AuthMode::ChatGPT => "ChatGPT",
- };
- let msg = format!(
- " Youre currently using {} while your preferred method is {}.",
- to_label(current),
- to_label(self.preferred_auth_method)
- );
- lines.push(Line::from(msg).style(Style::default()));
- lines.push(Line::from(""));
- }
+ if let LoginStatus::AuthMode(current) = self.login_status
+ && current != self.preferred_auth_method
+ {
+ let to_label = |mode: AuthMode| match mode {
+ AuthMode::ApiKey => "API key",
+ AuthMode::ChatGPT => "ChatGPT",
+ };
+ let msg = format!(
+ " Youre currently using {} while your preferred method is {}.",
+ to_label(current),
+ to_label(self.preferred_auth_method)
+ );
+ lines.push(Line::from(msg).style(Style::default()));
+ lines.push(Line::from(""));
}
let create_mode_item = |idx: usize,
@@ -222,15 +222,15 @@ impl AuthModeWidget {
spans.extend(shimmer_spans("Finish signing in via your browser"));
let mut lines = vec![Line::from(spans), Line::from("")];
- if let SignInState::ChatGptContinueInBrowser(state) = &self.sign_in_state {
- if !state.auth_url.is_empty() {
- lines.push(Line::from(" If the link doesn't open automatically, open the following link to authenticate:"));
- lines.push(Line::from(vec![
- Span::raw(" "),
- state.auth_url.as_str().cyan().underlined(),
- ]));
- lines.push(Line::from(""));
- }
+ if let SignInState::ChatGptContinueInBrowser(state) = &self.sign_in_state
+ && !state.auth_url.is_empty()
+ {
+ lines.push(Line::from(" If the link doesn't open automatically, open the following link to authenticate:"));
+ lines.push(Line::from(vec![
+ Span::raw(" "),
+ state.auth_url.as_str().cyan().underlined(),
+ ]));
+ lines.push(Line::from(""));
}
lines.push(
diff --git a/codex-rs/tui/src/streaming/controller.rs b/codex-rs/tui/src/streaming/controller.rs
index 161a111173..5eae2a91f6 100644
--- a/codex-rs/tui/src/streaming/controller.rs
+++ b/codex-rs/tui/src/streaming/controller.rs
@@ -96,26 +96,26 @@ impl StreamController {
/// Begin a stream, flushing previously completed lines from any other
/// active stream to maintain ordering.
pub(crate) fn begin(&mut self, kind: StreamKind, sink: &impl HistorySink) {
- if let Some(current) = self.current_stream {
- if current != kind {
- // Synchronously flush completed lines from previous stream.
- let cfg = self.config.clone();
- let prev_state = self.state_mut(current);
- let newly_completed = prev_state.collector.commit_complete_lines(&cfg);
- if !newly_completed.is_empty() {
- prev_state.enqueue(newly_completed);
- }
- let step = prev_state.drain_all();
- if !step.history.is_empty() {
- let mut lines: Lines = Vec::new();
- self.emit_header_if_needed(current, &mut lines);
- lines.extend(step.history);
- // Ensure at most one trailing blank after the flushed block.
- Self::ensure_single_trailing_blank(&mut lines);
- sink.insert_history(lines);
- }
- self.current_stream = None;
+ if let Some(current) = self.current_stream
+ && current != kind
+ {
+ // Synchronously flush completed lines from previous stream.
+ let cfg = self.config.clone();
+ let prev_state = self.state_mut(current);
+ let newly_completed = prev_state.collector.commit_complete_lines(&cfg);
+ if !newly_completed.is_empty() {
+ prev_state.enqueue(newly_completed);
+ }
+ let step = prev_state.drain_all();
+ if !step.history.is_empty() {
+ let mut lines: Lines = Vec::new();
+ self.emit_header_if_needed(current, &mut lines);
+ lines.extend(step.history);
+ // Ensure at most one trailing blank after the flushed block.
+ Self::ensure_single_trailing_blank(&mut lines);
+ sink.insert_history(lines);
}
+ self.current_stream = None;
}
if self.current_stream != Some(kind) {
diff --git a/codex-rs/tui/src/text_formatting.rs b/codex-rs/tui/src/text_formatting.rs
index b77c6e5baa..3c4cfc31bb 100644
--- a/codex-rs/tui/src/text_formatting.rs
+++ b/codex-rs/tui/src/text_formatting.rs
@@ -53,12 +53,12 @@ pub(crate) fn format_json_compact(text: &str) -> Option<String> {
}
' ' | '\t' if !in_string => {
// Add a space after : and , but only when not in a string
- if let Some(&next_ch) = chars.peek() {
- if let Some(last_ch) = result.chars().last() {
- if (last_ch == ':' || last_ch == ',') && !matches!(next_ch, '}' | ']') {
- result.push(' ');
- }
- }
+ if let Some(&next_ch) = chars.peek()
+ && let Some(last_ch) = result.chars().last()
+ && (last_ch == ':' || last_ch == ',')
+ && !matches!(next_ch, '}' | ']')
+ {
+ result.push(' ');
}
}
_ => {
diff --git a/codex-rs/tui/tests/vt100_history.rs b/codex-rs/tui/tests/vt100_history.rs
index 402e847b47..1f01a6d3ba 100644
--- a/codex-rs/tui/tests/vt100_history.rs
+++ b/codex-rs/tui/tests/vt100_history.rs
@@ -112,12 +112,11 @@ fn long_token_wraps() {
let mut count_a = 0usize;
for row in 0..6 {
for col in 0..20 {
- if let Some(cell) = screen.cell(row, col) {
- if let Some(ch) = cell.contents().chars().next() {
- if ch == 'A' {
- count_a += 1;
- }
- }
+ if let Some(cell) = screen.cell(row, col)
+ && let Some(ch) = cell.contents().chars().next()
+ && ch == 'A'
+ {
+ count_a += 1;
}
}
}
```
## Review Comments
### codex-rs/core/src/parse_command.rs
- Created: 2025-08-19 19:59:07 UTC | Link: https://github.com/openai/codex/pull/2465#discussion_r2286202114
```diff
@@ -1208,17 +1208,15 @@ fn simplify_once(commands: &[ParsedCommand]) -> Option<Vec<ParsedCommand>> {
shlex_split(cmd).is_some_and(|t| t.first().map(|s| s.as_str()) == Some("cd"))
}
_ => false,
- }) {
- if commands
- .iter()
- .skip(idx + 1)
- .any(|pc| matches!(pc, ParsedCommand::Test { .. }))
- {
- let mut out = Vec::with_capacity(commands.len() - 1);
- out.extend_from_slice(&commands[..idx]);
- out.extend_from_slice(&commands[idx + 1..]);
- return Some(out);
- }
+ }) && commands
```
> Understood, though it is a losing battle against Clippy (unless we choose to disable this rule?). I think, in general, avoiding the nested `if`s is for the best.
### codex-rs/tui/src/custom_terminal.rs
- Created: 2025-08-19 19:59:47 UTC | Link: https://github.com/openai/codex/pull/2465#discussion_r2286203165
```diff
@@ -309,7 +309,7 @@ where
}
/// Get a Frame object which provides a consistent view into the terminal state for rendering.
- pub fn get_frame(&mut self) -> Frame {
+ pub fn get_frame(&mut self) -> Frame<'_> {
```
> Good eye!
>
> Yes, it appears this has changed:
>
> https://blog.rust-lang.org/2025/08/07/Rust-1.89.0/#mismatched-lifetime-syntaxes-lint