From fb20a685f7d2dee60f842aae56a47fcfd7a6c663 Mon Sep 17 00:00:00 2001 From: Teakowa Date: Sat, 10 Oct 2026 06:54:27 +0800 Subject: [PATCH 1/2] fix(project): report no default entry when a path component is a file A directory whose src is a regular file made the src/main.opy probe fail ENOTDIR and surface as EntryUnreadable; upstream-equivalent behavior is no default entry, matching the empty-directory result and provider targetNotFound. A direct entry through a file keeps EntryUnreadable. Fixes #497 --- crates/opy-rs/src/project.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/crates/opy-rs/src/project.rs b/crates/opy-rs/src/project.rs index ab00481e..5bbc5486 100644 --- a/crates/opy-rs/src/project.rs +++ b/crates/opy-rs/src/project.rs @@ -70,7 +70,14 @@ fn default_entry(directory: &Path) -> Result { // Present but not a regular file, or genuinely absent — keep // looking; only a real I/O failure aborts classification (#484). Ok(_) => {} - Err(error) if error.kind() == io::ErrorKind::NotFound => {} + // A path component that is a file (`NotADirectory`, `NotFound` on + // Windows) means the candidate cannot exist, like an absent one + // (#497); only a real I/O failure aborts classification. + Err(error) + if matches!( + error.kind(), + io::ErrorKind::NotFound | io::ErrorKind::NotADirectory + ) => {} Err(error) => { return Err(FilesystemProjectError::entry_unreadable(candidate, error)); } @@ -239,6 +246,23 @@ mod tests { )); } + /// A directory holding a regular file named `src` has no default entry, + /// like an empty directory (#497). Unix reports `NotADirectory` for the + /// `src/main.opy` probe; Windows reports `NotFound` — the test asserts + /// only the shared outcome, verified on macOS. + #[test] + fn file_named_src_in_a_directory_is_default_entry_not_found() { + let dir = scratch("file-src"); + std::fs::write(dir.join("src"), "not a directory\n").unwrap(); + let error = FilesystemProject::load(&dir).expect_err("no default entry"); + assert!(error.is_entry_not_found()); + assert!(matches!( + error, + FilesystemProjectError::DefaultEntryNotFound { .. } + )); + std::fs::remove_dir_all(&dir).unwrap(); + } + /// A path inside a regular file fails canonicalization with `ENOTDIR` /// on every platform — the deterministic non-`NotFound` cause. #[test] From d16993221455b66a0950fcacf42c7dd027c9bb39 Mon Sep 17 00:00:00 2001 From: Teakowa Date: Sat, 10 Oct 2026 06:59:43 +0800 Subject: [PATCH 2/2] docs(project): correct the ENOTDIR platform claim on the neighboring test --- crates/opy-rs/src/project.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/opy-rs/src/project.rs b/crates/opy-rs/src/project.rs index 5bbc5486..99dfbe46 100644 --- a/crates/opy-rs/src/project.rs +++ b/crates/opy-rs/src/project.rs @@ -263,8 +263,9 @@ mod tests { std::fs::remove_dir_all(&dir).unwrap(); } - /// A path inside a regular file fails canonicalization with `ENOTDIR` - /// on every platform — the deterministic non-`NotFound` cause. + /// A path inside a regular file fails canonicalization with `ENOTDIR` on + /// Unix (`NotFound` on Windows) — a deterministic non-`NotFound` cause + /// here; verified on macOS. #[test] fn entry_inside_a_file_is_not_classified_as_missing() { let dir = scratch("notdir");