From ec3738b47e3d88b39261ddcdbcb26850971a61c0 Mon Sep 17 00:00:00 2001 From: RQfreefly <53940557+RQfreefly@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:50:55 +0800 Subject: [PATCH] feat: move file name derivation into codex-file-search (#8334) ## Summary - centralize file name derivation in codex-file-search - reuse the helper in app-server fuzzy search to avoid duplicate logic - add unit tests for file_name_from_path ## Testing - cargo test -p codex-file-search - cargo test -p codex-app-server --- codex-rs/Cargo.lock | 1 + codex-rs/app-server/src/fuzzy_file_search.rs | 7 +------ codex-rs/file-search/Cargo.toml | 3 +++ codex-rs/file-search/src/lib.rs | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 178149e63..12581d33e 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1454,6 +1454,7 @@ dependencies = [ "clap", "ignore", "nucleo-matcher", + "pretty_assertions", "serde", "serde_json", "tokio", diff --git a/codex-rs/app-server/src/fuzzy_file_search.rs b/codex-rs/app-server/src/fuzzy_file_search.rs index 5c6d86e18..eb3dfe00b 100644 --- a/codex-rs/app-server/src/fuzzy_file_search.rs +++ b/codex-rs/app-server/src/fuzzy_file_search.rs @@ -1,6 +1,5 @@ use std::num::NonZero; use std::num::NonZeroUsize; -use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::AtomicBool; @@ -63,11 +62,7 @@ pub(crate) async fn run_fuzzy_file_search( Ok(Ok((root, res))) => { for m in res.matches { let path = m.path; - //TODO(shijie): Move file name generation to file_search lib. - let file_name = Path::new(&path) - .file_name() - .map(|name| name.to_string_lossy().into_owned()) - .unwrap_or_else(|| path.clone()); + let file_name = file_search::file_name_from_path(&path); let result = FuzzyFileSearchResult { root: root.clone(), path, diff --git a/codex-rs/file-search/Cargo.toml b/codex-rs/file-search/Cargo.toml index e0dea1c13..70ddcf2bb 100644 --- a/codex-rs/file-search/Cargo.toml +++ b/codex-rs/file-search/Cargo.toml @@ -20,3 +20,6 @@ nucleo-matcher = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tokio = { workspace = true, features = ["full"] } + +[dev-dependencies] +pretty_assertions = { workspace = true } diff --git a/codex-rs/file-search/src/lib.rs b/codex-rs/file-search/src/lib.rs index 0afc9ea6a..d55eb929f 100644 --- a/codex-rs/file-search/src/lib.rs +++ b/codex-rs/file-search/src/lib.rs @@ -40,6 +40,14 @@ pub struct FileMatch { pub indices: Option>, // Sorted & deduplicated when present } +/// Returns the final path component for a matched path, falling back to the full path. +pub fn file_name_from_path(path: &str) -> String { + Path::new(path) + .file_name() + .map(|name| name.to_string_lossy().into_owned()) + .unwrap_or_else(|| path.to_string()) +} + #[derive(Debug)] pub struct FileSearchResults { pub matches: Vec, @@ -403,6 +411,7 @@ fn create_pattern(pattern: &str) -> Pattern { #[cfg(test)] mod tests { use super::*; + use pretty_assertions::assert_eq; #[test] fn verify_score_is_none_for_non_match() { @@ -434,4 +443,14 @@ mod tests { assert_eq!(matches, expected); } + + #[test] + fn file_name_from_path_uses_basename() { + assert_eq!(file_name_from_path("foo/bar.txt"), "bar.txt"); + } + + #[test] + fn file_name_from_path_falls_back_to_full_path() { + assert_eq!(file_name_from_path(""), ""); + } }