1use std::path::Path;
16
17pub fn read_working_file(repo_path: &Path, rel_path: &str) -> std::io::Result<Option<String>> {
21 match std::fs::read(repo_path.join(rel_path)) {
22 Ok(bytes) => Ok(Some(String::from_utf8_lossy(&bytes).into_owned())),
23 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
24 Err(e) => Err(e),
25 }
26}
27
28pub fn read_working_file_bytes(
30 repo_path: &Path,
31 rel_path: &str,
32) -> std::io::Result<Option<Vec<u8>>> {
33 match std::fs::read(repo_path.join(rel_path)) {
34 Ok(bytes) => Ok(Some(bytes)),
35 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
36 Err(e) => Err(e),
37 }
38}
39
40pub fn working_file_is_binary(repo_path: &Path, rel_path: &str) -> bool {
44 use std::io::Read;
45 let Ok(f) = std::fs::File::open(repo_path.join(rel_path)) else {
46 return false;
47 };
48 let mut buf = Vec::with_capacity(8192);
52 if f.take(8192).read_to_end(&mut buf).is_err() {
53 return false;
54 }
55 buf.contains(&0)
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn reads_present_file_and_none_for_absent() {
64 let dir = tempfile::tempdir().unwrap();
65 std::fs::write(dir.path().join("a.txt"), "hello\n").unwrap();
66 assert_eq!(
67 read_working_file(dir.path(), "a.txt").unwrap().as_deref(),
68 Some("hello\n")
69 );
70 assert_eq!(read_working_file(dir.path(), "missing.txt").unwrap(), None);
71 }
72
73 #[test]
74 fn bytes_variant_round_trips_and_none_for_absent() {
75 let dir = tempfile::tempdir().unwrap();
76 std::fs::write(dir.path().join("b.bin"), [0u8, 1, 2, 255]).unwrap();
77 assert_eq!(
78 read_working_file_bytes(dir.path(), "b.bin").unwrap(),
79 Some(vec![0, 1, 2, 255])
80 );
81 assert_eq!(read_working_file_bytes(dir.path(), "nope").unwrap(), None);
82 }
83
84 #[test]
85 fn binary_detection_flags_nul_and_clears_text() {
86 let dir = tempfile::tempdir().unwrap();
87 std::fs::write(dir.path().join("t.txt"), "plain text\n").unwrap();
88 std::fs::write(dir.path().join("b.bin"), [b'x', 0, b'y']).unwrap();
89 assert!(!working_file_is_binary(dir.path(), "t.txt"));
90 assert!(working_file_is_binary(dir.path(), "b.bin"));
91 assert!(!working_file_is_binary(dir.path(), "absent"));
92 }
93
94 #[test]
95 fn binary_detection_scans_the_full_8kib_window() {
96 let dir = tempfile::tempdir().unwrap();
97 let mut data = vec![b'a'; 8000];
100 data.push(0);
101 std::fs::write(dir.path().join("mid.bin"), &data).unwrap();
102 assert!(working_file_is_binary(dir.path(), "mid.bin"));
103 }
104
105 #[test]
106 fn binary_detection_stops_at_8kib() {
107 let dir = tempfile::tempdir().unwrap();
108 let mut data = vec![b'a'; 8192];
110 data.push(0);
111 std::fs::write(dir.path().join("late.bin"), &data).unwrap();
112 assert!(!working_file_is_binary(dir.path(), "late.bin"));
113 }
114}