1use std::io;
7use std::path::{Path, PathBuf};
8
9use crate::ascii::cmp_ascii_case_insensitive;
10
11pub fn find_case_insensitive_file(
18 directory: &Path,
19 expected_file_name: &str,
20) -> io::Result<Option<PathBuf>> {
21 let mut matches = Vec::new();
22 let entries = std::fs::read_dir(directory)?;
23 for entry in entries {
24 let entry = entry?;
25 let path = entry.path();
26 if !path.is_file() {
27 continue;
28 }
29 let Some(name) = path.file_name().and_then(|name| name.to_str()) else {
30 continue;
31 };
32 if name.eq_ignore_ascii_case(expected_file_name) {
33 matches.push(path);
34 }
35 }
36
37 if matches.is_empty() {
38 return Ok(None);
39 }
40
41 matches.sort_by(|a, b| {
42 let a_name = a
43 .file_name()
44 .and_then(|name| name.to_str())
45 .unwrap_or_default();
46 let b_name = b
47 .file_name()
48 .and_then(|name| name.to_str())
49 .unwrap_or_default();
50 cmp_ascii_case_insensitive(a_name, b_name).then(a_name.cmp(b_name))
51 });
52
53 Ok(matches.into_iter().next())
54}
55
56#[cfg(test)]
57mod tests {
58 use super::find_case_insensitive_file;
59
60 use std::fs;
61
62 use tempfile::TempDir;
63
64 #[test]
65 fn resolves_case_insensitive_match() {
66 let temp = TempDir::new().expect("create tempdir");
67 let root = temp.path();
68 let path = root.join("ChItIn.KeY");
69 fs::write(&path, b"key").expect("write fixture");
70
71 let found = find_case_insensitive_file(root, "chitin.key")
72 .expect("lookup should succeed")
73 .expect("file should be found");
74 assert_eq!(found, path);
75 }
76
77 #[test]
78 fn returns_none_when_no_match() {
79 let temp = TempDir::new().expect("create tempdir");
80
81 let found =
82 find_case_insensitive_file(temp.path(), "missing.txt").expect("lookup should run");
83 assert!(found.is_none());
84 }
85}