1use crate::diagnostics::{LintError, LintResult};
32use std::collections::HashMap;
33use std::fs::Metadata;
34use std::io;
35use std::path::{Path, PathBuf};
36use std::sync::RwLock;
37
38#[derive(Debug, Clone)]
42pub struct FileMetadata {
43 pub is_file: bool,
45 pub is_dir: bool,
47 pub is_symlink: bool,
49 pub len: u64,
51}
52
53impl FileMetadata {
54 pub fn file(len: u64) -> Self {
56 Self {
57 is_file: true,
58 is_dir: false,
59 is_symlink: false,
60 len,
61 }
62 }
63
64 pub fn directory() -> Self {
66 Self {
67 is_file: false,
68 is_dir: true,
69 is_symlink: false,
70 len: 0,
71 }
72 }
73
74 pub fn symlink() -> Self {
76 Self {
77 is_file: false,
78 is_dir: false,
79 is_symlink: true,
80 len: 0,
81 }
82 }
83}
84
85impl From<&Metadata> for FileMetadata {
86 fn from(meta: &Metadata) -> Self {
87 Self {
88 is_file: meta.is_file(),
89 is_dir: meta.is_dir(),
90 is_symlink: meta.file_type().is_symlink(),
91 len: meta.len(),
92 }
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct DirEntry {
99 pub path: PathBuf,
101 pub metadata: FileMetadata,
103}
104
105pub trait FileSystem: Send + Sync + std::fmt::Debug {
110 fn exists(&self, path: &Path) -> bool;
112
113 fn is_file(&self, path: &Path) -> bool;
115
116 fn is_dir(&self, path: &Path) -> bool;
118
119 fn is_symlink(&self, path: &Path) -> bool;
121
122 fn metadata(&self, path: &Path) -> io::Result<FileMetadata>;
124
125 fn symlink_metadata(&self, path: &Path) -> io::Result<FileMetadata>;
127
128 fn read_to_string(&self, path: &Path) -> LintResult<String>;
130
131 fn write(&self, path: &Path, content: &str) -> LintResult<()>;
133
134 fn canonicalize(&self, path: &Path) -> io::Result<PathBuf>;
136
137 fn read_dir(&self, path: &Path) -> io::Result<Vec<DirEntry>>;
139}
140
141#[derive(Debug, Clone, Copy, Default)]
143pub struct RealFileSystem;
144
145impl FileSystem for RealFileSystem {
146 fn exists(&self, path: &Path) -> bool {
147 path.exists()
148 }
149
150 fn is_file(&self, path: &Path) -> bool {
151 path.is_file()
152 }
153
154 fn is_dir(&self, path: &Path) -> bool {
155 path.is_dir()
156 }
157
158 fn is_symlink(&self, path: &Path) -> bool {
159 path.is_symlink()
160 }
161
162 fn metadata(&self, path: &Path) -> io::Result<FileMetadata> {
163 std::fs::metadata(path).map(|m| FileMetadata::from(&m))
164 }
165
166 fn symlink_metadata(&self, path: &Path) -> io::Result<FileMetadata> {
167 std::fs::symlink_metadata(path).map(|m| FileMetadata::from(&m))
168 }
169
170 fn read_to_string(&self, path: &Path) -> LintResult<String> {
171 crate::file_utils::safe_read_file(path)
172 }
173
174 fn write(&self, path: &Path, content: &str) -> LintResult<()> {
175 crate::file_utils::safe_write_file(path, content)
176 }
177
178 fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
179 std::fs::canonicalize(path)
180 }
181
182 fn read_dir(&self, path: &Path) -> io::Result<Vec<DirEntry>> {
183 Ok(std::fs::read_dir(path)?
184 .filter_map(|entry_res| {
185 let entry = entry_res.ok()?;
188 let path = entry.path();
189 let metadata = std::fs::symlink_metadata(&path).ok()?;
192 Some(DirEntry {
193 path,
194 metadata: FileMetadata::from(&metadata),
195 })
196 })
197 .collect())
198 }
199}
200
201#[derive(Debug, Clone)]
203enum MockEntry {
204 File { content: String },
205 Directory,
206 Symlink { target: PathBuf },
207}
208
209#[derive(Debug, Default)]
214pub struct MockFileSystem {
215 entries: RwLock<HashMap<PathBuf, MockEntry>>,
216}
217
218impl MockFileSystem {
219 pub fn new() -> Self {
221 Self {
222 entries: RwLock::new(HashMap::new()),
223 }
224 }
225
226 pub fn add_file(&self, path: impl AsRef<Path>, content: impl Into<String>) {
228 let path = normalize_mock_path(path.as_ref());
229 let mut entries = self.entries.write().expect("MockFileSystem lock poisoned");
230 entries.insert(
231 path,
232 MockEntry::File {
233 content: content.into(),
234 },
235 );
236 }
237
238 pub fn add_dir(&self, path: impl AsRef<Path>) {
240 let path = normalize_mock_path(path.as_ref());
241 let mut entries = self.entries.write().expect("MockFileSystem lock poisoned");
242 entries.insert(path, MockEntry::Directory);
243 }
244
245 pub fn add_symlink(&self, path: impl AsRef<Path>, target: impl AsRef<Path>) {
247 let path = normalize_mock_path(path.as_ref());
248 let target = normalize_mock_path(target.as_ref());
249 let mut entries = self.entries.write().expect("MockFileSystem lock poisoned");
250 entries.insert(path, MockEntry::Symlink { target });
251 }
252
253 pub fn remove(&self, path: impl AsRef<Path>) {
255 let path = normalize_mock_path(path.as_ref());
256 let mut entries = self.entries.write().expect("MockFileSystem lock poisoned");
257 entries.remove(&path);
258 }
259
260 pub fn clear(&self) {
262 let mut entries = self.entries.write().expect("MockFileSystem lock poisoned");
263 entries.clear();
264 }
265
266 fn get_entry(&self, path: &Path) -> Option<MockEntry> {
267 let path = normalize_mock_path(path);
268 let entries = self.entries.read().expect("MockFileSystem lock poisoned");
269 entries.get(&path).cloned()
270 }
271
272 fn resolve_symlink(&self, path: &Path) -> Option<PathBuf> {
273 let path = normalize_mock_path(path);
274 let entries = self.entries.read().expect("MockFileSystem lock poisoned");
275 match entries.get(&path) {
276 Some(MockEntry::Symlink { target }) => Some(target.clone()),
277 _ => None,
278 }
279 }
280
281 const MAX_SYMLINK_DEPTH: u32 = 40;
283
284 fn metadata_with_depth(&self, path: &Path, depth: u32) -> io::Result<FileMetadata> {
286 if depth > Self::MAX_SYMLINK_DEPTH {
287 return Err(io::Error::other("too many levels of symbolic links"));
288 }
289
290 enum MetaResult {
292 Found(FileMetadata),
293 FollowSymlink(PathBuf),
294 }
295
296 let path = normalize_mock_path(path);
297
298 let result: io::Result<MetaResult> = {
299 let entries = self.entries.read().expect("MockFileSystem lock poisoned");
300 match entries.get(&path) {
301 None => Err(io::Error::new(
302 io::ErrorKind::NotFound,
303 format!("path not found: {}", path.display()),
304 )),
305 Some(MockEntry::File { content }) => {
306 Ok(MetaResult::Found(FileMetadata::file(content.len() as u64)))
307 }
308 Some(MockEntry::Directory) => Ok(MetaResult::Found(FileMetadata::directory())),
309 Some(MockEntry::Symlink { target }) => {
310 Ok(MetaResult::FollowSymlink(target.clone()))
311 }
312 }
313 };
314
315 match result? {
316 MetaResult::Found(meta) => Ok(meta),
317 MetaResult::FollowSymlink(target) => self.metadata_with_depth(&target, depth + 1),
318 }
319 }
320
321 fn canonicalize_with_depth(&self, path: &Path, depth: u32) -> io::Result<PathBuf> {
323 if depth > Self::MAX_SYMLINK_DEPTH {
324 return Err(io::Error::other("too many levels of symbolic links"));
325 }
326
327 let path_normalized = normalize_mock_path(path);
328
329 if !self.exists(&path_normalized) {
330 return Err(io::Error::new(
331 io::ErrorKind::NotFound,
332 format!("path not found: {}", path.display()),
333 ));
334 }
335
336 if let Some(target) = self.resolve_symlink(&path_normalized) {
338 self.canonicalize_with_depth(&target, depth + 1)
339 } else {
340 Ok(path_normalized)
341 }
342 }
343}
344
345fn normalize_mock_path(path: &Path) -> PathBuf {
348 let path_str = path.to_string_lossy();
349 PathBuf::from(path_str.replace('\\', "/"))
350}
351
352impl FileSystem for MockFileSystem {
353 fn exists(&self, path: &Path) -> bool {
354 self.get_entry(path).is_some()
355 }
356
357 fn is_file(&self, path: &Path) -> bool {
358 matches!(self.get_entry(path), Some(MockEntry::File { .. }))
359 }
360
361 fn is_dir(&self, path: &Path) -> bool {
362 matches!(self.get_entry(path), Some(MockEntry::Directory))
363 }
364
365 fn is_symlink(&self, path: &Path) -> bool {
366 matches!(self.get_entry(path), Some(MockEntry::Symlink { .. }))
367 }
368
369 fn metadata(&self, path: &Path) -> io::Result<FileMetadata> {
370 self.metadata_with_depth(path, 0)
371 }
372
373 fn symlink_metadata(&self, path: &Path) -> io::Result<FileMetadata> {
374 let entry = self.get_entry(path).ok_or_else(|| {
376 io::Error::new(
377 io::ErrorKind::NotFound,
378 format!("path not found: {}", path.display()),
379 )
380 })?;
381
382 match entry {
383 MockEntry::File { content } => Ok(FileMetadata::file(content.len() as u64)),
384 MockEntry::Directory => Ok(FileMetadata::directory()),
385 MockEntry::Symlink { .. } => Ok(FileMetadata::symlink()),
386 }
387 }
388
389 fn read_to_string(&self, path: &Path) -> LintResult<String> {
390 let path_normalized = normalize_mock_path(path);
391 let entries = self.entries.read().expect("MockFileSystem lock poisoned");
392
393 let entry = entries
394 .get(&path_normalized)
395 .ok_or_else(|| LintError::FileRead {
396 path: path.to_path_buf(),
397 source: io::Error::new(io::ErrorKind::NotFound, "file not found"),
398 })?;
399
400 match entry {
401 MockEntry::File { content } => Ok(content.clone()),
402 MockEntry::Directory => Err(LintError::FileNotRegular {
403 path: path.to_path_buf(),
404 }),
405 MockEntry::Symlink { .. } => Err(LintError::FileSymlink {
406 path: path.to_path_buf(),
407 }),
408 }
409 }
410
411 fn write(&self, path: &Path, content: &str) -> LintResult<()> {
412 let path_normalized = normalize_mock_path(path);
413 let mut entries = self.entries.write().expect("MockFileSystem lock poisoned");
414
415 match entries.get(&path_normalized) {
417 Some(MockEntry::File { .. }) => {
418 entries.insert(
420 path_normalized,
421 MockEntry::File {
422 content: content.to_string(),
423 },
424 );
425 Ok(())
426 }
427 Some(MockEntry::Directory) => Err(LintError::FileNotRegular {
428 path: path.to_path_buf(),
429 }),
430 Some(MockEntry::Symlink { .. }) => Err(LintError::FileSymlink {
431 path: path.to_path_buf(),
432 }),
433 None => {
434 Err(LintError::FileWrite {
436 path: path.to_path_buf(),
437 source: io::Error::new(io::ErrorKind::NotFound, "file not found"),
438 })
439 }
440 }
441 }
442
443 fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
444 self.canonicalize_with_depth(path, 0)
445 }
446
447 fn read_dir(&self, path: &Path) -> io::Result<Vec<DirEntry>> {
448 let path_normalized = normalize_mock_path(path);
449
450 match self.get_entry(&path_normalized) {
452 Some(MockEntry::Directory) => {}
453 Some(_) => {
454 return Err(io::Error::new(
455 io::ErrorKind::NotADirectory,
456 "not a directory",
457 ));
458 }
459 None => {
460 return Err(io::Error::new(
461 io::ErrorKind::NotFound,
462 "directory not found",
463 ));
464 }
465 }
466
467 let entries = self.entries.read().expect("MockFileSystem lock poisoned");
468 let mut result = Vec::new();
469
470 let prefix = if path_normalized.to_string_lossy().ends_with('/') {
472 path_normalized.to_string_lossy().to_string()
473 } else {
474 format!("{}/", path_normalized.display())
475 };
476
477 for (entry_path, entry) in entries.iter() {
478 let entry_str = entry_path.to_string_lossy();
479
480 if let Some(rest) = entry_str.strip_prefix(&prefix) {
482 if !rest.contains('/') && !rest.is_empty() {
484 let metadata = match entry {
485 MockEntry::File { content } => FileMetadata::file(content.len() as u64),
486 MockEntry::Directory => FileMetadata::directory(),
487 MockEntry::Symlink { .. } => FileMetadata::symlink(),
488 };
489 result.push(DirEntry {
490 path: entry_path.clone(),
491 metadata,
492 });
493 }
494 }
495 }
496
497 Ok(result)
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504
505 #[test]
508 fn test_real_fs_exists() {
509 let fs = RealFileSystem;
510 assert!(fs.exists(Path::new("Cargo.toml")));
512 assert!(!fs.exists(Path::new("nonexistent_file_xyz.txt")));
513 }
514
515 #[test]
516 fn test_real_fs_is_file() {
517 let fs = RealFileSystem;
518 assert!(fs.is_file(Path::new("Cargo.toml")));
519 assert!(!fs.is_file(Path::new("src")));
520 }
521
522 #[test]
523 fn test_real_fs_is_dir() {
524 let fs = RealFileSystem;
525 assert!(fs.is_dir(Path::new("src")));
526 assert!(!fs.is_dir(Path::new("Cargo.toml")));
527 }
528
529 #[test]
530 fn test_real_fs_read_to_string() {
531 let fs = RealFileSystem;
532 let content = fs.read_to_string(Path::new("Cargo.toml"));
533 assert!(content.is_ok());
534 assert!(content.unwrap().contains("[package]"));
535 }
536
537 #[test]
538 fn test_real_fs_read_nonexistent() {
539 let fs = RealFileSystem;
540 let result = fs.read_to_string(Path::new("nonexistent_file_xyz.txt"));
541 assert!(result.is_err());
542 }
543
544 #[test]
547 fn test_mock_fs_add_and_exists() {
548 let fs = MockFileSystem::new();
549 assert!(!fs.exists(Path::new("/test/file.txt")));
550
551 fs.add_file("/test/file.txt", "content");
552 assert!(fs.exists(Path::new("/test/file.txt")));
553 }
554
555 #[test]
556 fn test_mock_fs_is_file() {
557 let fs = MockFileSystem::new();
558 fs.add_file("/test/file.txt", "content");
559 fs.add_dir("/test/dir");
560
561 assert!(fs.is_file(Path::new("/test/file.txt")));
562 assert!(!fs.is_file(Path::new("/test/dir")));
563 }
564
565 #[test]
566 fn test_mock_fs_is_dir() {
567 let fs = MockFileSystem::new();
568 fs.add_file("/test/file.txt", "content");
569 fs.add_dir("/test/dir");
570
571 assert!(!fs.is_dir(Path::new("/test/file.txt")));
572 assert!(fs.is_dir(Path::new("/test/dir")));
573 }
574
575 #[test]
576 fn test_mock_fs_is_symlink() {
577 let fs = MockFileSystem::new();
578 fs.add_file("/test/file.txt", "content");
579 fs.add_symlink("/test/link.txt", "/test/file.txt");
580
581 assert!(!fs.is_symlink(Path::new("/test/file.txt")));
582 assert!(fs.is_symlink(Path::new("/test/link.txt")));
583 }
584
585 #[test]
586 fn test_mock_fs_read_to_string() {
587 let fs = MockFileSystem::new();
588 fs.add_file("/test/file.txt", "hello world");
589
590 let content = fs.read_to_string(Path::new("/test/file.txt"));
591 assert!(content.is_ok());
592 assert_eq!(content.unwrap(), "hello world");
593 }
594
595 #[test]
596 fn test_mock_fs_read_nonexistent() {
597 let fs = MockFileSystem::new();
598 let result = fs.read_to_string(Path::new("/test/file.txt"));
599 assert!(result.is_err());
600 }
601
602 #[test]
603 fn test_mock_fs_read_directory_fails() {
604 let fs = MockFileSystem::new();
605 fs.add_dir("/test/dir");
606
607 let result = fs.read_to_string(Path::new("/test/dir"));
608 assert!(matches!(result, Err(LintError::FileNotRegular { .. })));
609 }
610
611 #[test]
612 fn test_mock_fs_read_symlink_fails() {
613 let fs = MockFileSystem::new();
614 fs.add_file("/test/file.txt", "content");
615 fs.add_symlink("/test/link.txt", "/test/file.txt");
616
617 let result = fs.read_to_string(Path::new("/test/link.txt"));
618 assert!(matches!(result, Err(LintError::FileSymlink { .. })));
619 }
620
621 #[test]
622 fn test_mock_fs_write() {
623 let fs = MockFileSystem::new();
624 fs.add_file("/test/file.txt", "original");
625
626 let result = fs.write(Path::new("/test/file.txt"), "updated");
627 assert!(result.is_ok());
628
629 let content = fs.read_to_string(Path::new("/test/file.txt")).unwrap();
630 assert_eq!(content, "updated");
631 }
632
633 #[test]
634 fn test_mock_fs_write_nonexistent_fails() {
635 let fs = MockFileSystem::new();
636
637 let result = fs.write(Path::new("/test/file.txt"), "content");
638 assert!(matches!(result, Err(LintError::FileWrite { .. })));
639 }
640
641 #[test]
642 fn test_mock_fs_metadata_file() {
643 let fs = MockFileSystem::new();
644 fs.add_file("/test/file.txt", "12345");
645
646 let meta = fs.metadata(Path::new("/test/file.txt")).unwrap();
647 assert!(meta.is_file);
648 assert!(!meta.is_dir);
649 assert!(!meta.is_symlink);
650 assert_eq!(meta.len, 5);
651 }
652
653 #[test]
654 fn test_mock_fs_metadata_directory() {
655 let fs = MockFileSystem::new();
656 fs.add_dir("/test/dir");
657
658 let meta = fs.metadata(Path::new("/test/dir")).unwrap();
659 assert!(!meta.is_file);
660 assert!(meta.is_dir);
661 assert!(!meta.is_symlink);
662 }
663
664 #[test]
665 fn test_mock_fs_symlink_metadata() {
666 let fs = MockFileSystem::new();
667 fs.add_file("/test/file.txt", "content");
668 fs.add_symlink("/test/link.txt", "/test/file.txt");
669
670 let meta = fs.symlink_metadata(Path::new("/test/link.txt")).unwrap();
672 assert!(meta.is_symlink);
673
674 let meta = fs.metadata(Path::new("/test/link.txt")).unwrap();
676 assert!(meta.is_file);
677 assert!(!meta.is_symlink);
678 }
679
680 #[test]
681 fn test_mock_fs_read_dir() {
682 let fs = MockFileSystem::new();
683 fs.add_dir("/test");
684 fs.add_file("/test/file1.txt", "content1");
685 fs.add_file("/test/file2.txt", "content2");
686 fs.add_dir("/test/subdir");
687
688 let entries = fs.read_dir(Path::new("/test")).unwrap();
689 assert_eq!(entries.len(), 3);
690
691 let names: Vec<_> = entries
692 .iter()
693 .map(|e| e.path.file_name().unwrap().to_string_lossy().to_string())
694 .collect();
695 assert!(names.contains(&"file1.txt".to_string()));
696 assert!(names.contains(&"file2.txt".to_string()));
697 assert!(names.contains(&"subdir".to_string()));
698 }
699
700 #[test]
701 fn test_mock_fs_read_dir_nonexistent() {
702 let fs = MockFileSystem::new();
703 let result = fs.read_dir(Path::new("/nonexistent"));
704 assert!(result.is_err());
705 }
706
707 #[test]
708 fn test_mock_fs_read_dir_not_directory() {
709 let fs = MockFileSystem::new();
710 fs.add_file("/test/file.txt", "content");
711
712 let result = fs.read_dir(Path::new("/test/file.txt"));
713 assert!(result.is_err());
714 }
715
716 #[test]
717 fn test_mock_fs_canonicalize() {
718 let fs = MockFileSystem::new();
719 fs.add_file("/test/file.txt", "content");
720
721 let canonical = fs.canonicalize(Path::new("/test/file.txt")).unwrap();
722 assert_eq!(canonical, PathBuf::from("/test/file.txt"));
723 }
724
725 #[test]
726 fn test_mock_fs_canonicalize_follows_symlink() {
727 let fs = MockFileSystem::new();
728 fs.add_file("/test/file.txt", "content");
729 fs.add_symlink("/test/link.txt", "/test/file.txt");
730
731 let canonical = fs.canonicalize(Path::new("/test/link.txt")).unwrap();
732 assert_eq!(canonical, PathBuf::from("/test/file.txt"));
733 }
734
735 #[test]
736 fn test_mock_fs_clear() {
737 let fs = MockFileSystem::new();
738 fs.add_file("/test/file.txt", "content");
739 assert!(fs.exists(Path::new("/test/file.txt")));
740
741 fs.clear();
742 assert!(!fs.exists(Path::new("/test/file.txt")));
743 }
744
745 #[test]
746 fn test_mock_fs_remove() {
747 let fs = MockFileSystem::new();
748 fs.add_file("/test/file.txt", "content");
749 assert!(fs.exists(Path::new("/test/file.txt")));
750
751 fs.remove("/test/file.txt");
752 assert!(!fs.exists(Path::new("/test/file.txt")));
753 }
754
755 #[test]
756 fn test_mock_fs_windows_path_normalization() {
757 let fs = MockFileSystem::new();
758 fs.add_file("C:/test/file.txt", "content");
759
760 assert!(fs.exists(Path::new("C:/test/file.txt")));
762 assert!(fs.exists(Path::new("C:\\test\\file.txt")));
763 }
764
765 #[test]
766 fn test_mock_fs_thread_safety() {
767 use std::sync::Arc;
768 use std::thread;
769
770 let fs = Arc::new(MockFileSystem::new());
771 let mut handles = vec![];
772
773 for i in 0..10 {
775 let fs_clone = Arc::clone(&fs);
776 let handle = thread::spawn(move || {
777 let path = format!("/test/file{}.txt", i);
778 fs_clone.add_file(&path, format!("content{}", i));
779 assert!(fs_clone.exists(Path::new(&path)));
780 let content = fs_clone.read_to_string(Path::new(&path)).unwrap();
781 assert_eq!(content, format!("content{}", i));
782 });
783 handles.push(handle);
784 }
785
786 for handle in handles {
787 handle.join().unwrap();
788 }
789
790 for i in 0..10 {
792 let path = format!("/test/file{}.txt", i);
793 assert!(fs.exists(Path::new(&path)));
794 }
795 }
796
797 #[test]
798 fn test_mock_fs_circular_symlink_metadata() {
799 let fs = MockFileSystem::new();
800 fs.add_symlink("/test/a", "/test/b");
802 fs.add_symlink("/test/b", "/test/a");
803
804 let result = fs.metadata(Path::new("/test/a"));
806 assert!(result.is_err());
807 assert!(result
808 .unwrap_err()
809 .to_string()
810 .contains("too many levels of symbolic links"));
811 }
812
813 #[test]
814 fn test_mock_fs_circular_symlink_canonicalize() {
815 let fs = MockFileSystem::new();
816 fs.add_symlink("/test/a", "/test/b");
818 fs.add_symlink("/test/b", "/test/a");
819
820 let result = fs.canonicalize(Path::new("/test/a"));
822 assert!(result.is_err());
823 assert!(result
824 .unwrap_err()
825 .to_string()
826 .contains("too many levels of symbolic links"));
827 }
828
829 #[test]
830 fn test_mock_fs_chained_symlinks() {
831 let fs = MockFileSystem::new();
832 fs.add_file("/test/file.txt", "content");
834 fs.add_symlink("/test/link3", "/test/file.txt");
835 fs.add_symlink("/test/link2", "/test/link3");
836 fs.add_symlink("/test/link1", "/test/link2");
837
838 let meta = fs.metadata(Path::new("/test/link1")).unwrap();
840 assert!(meta.is_file);
841 assert_eq!(meta.len, 7); let canonical = fs.canonicalize(Path::new("/test/link1")).unwrap();
845 assert_eq!(canonical, PathBuf::from("/test/file.txt"));
846 }
847}