1use crate::errors::CoreError;
2use std::fs::{self, DirEntry, Metadata};
3use std::io;
4use std::path::{Path, PathBuf};
5use std::time::SystemTime;
6
7pub trait FileSystem: Send + Sync {
12 fn now(&self) -> SystemTime;
14
15 fn exists(&self, path: &Path) -> bool;
17
18 fn metadata(&self, path: &Path) -> crate::Result<Metadata>;
20
21 fn symlink_metadata(&self, path: &Path) -> crate::Result<Metadata>;
23
24 fn create_dir_all(&self, path: &Path) -> crate::Result<()>;
26
27 fn create_dir(&self, path: &Path) -> crate::Result<()>;
29
30 fn write(&self, path: &Path, data: &[u8]) -> crate::Result<()>;
32
33 fn write_to_string(&self, path: &Path, content: &str) -> crate::Result<()>;
35
36 fn read_to_string(&self, path: &Path) -> crate::Result<String>;
38
39 fn remove_file(&self, path: &Path) -> crate::Result<()>;
41
42 fn rename(&self, from: &Path, to: &Path) -> crate::Result<()>;
44
45 fn list_dir(&self, path: &Path) -> crate::Result<Vec<PathBuf>>;
47
48 fn remove_dir(&self, path: &Path) -> crate::Result<()>;
50}
51
52#[derive(Debug, Default, Clone, Copy)]
54pub struct RealFileSystem;
55
56impl FileSystem for RealFileSystem {
57 fn now(&self) -> SystemTime {
58 SystemTime::now()
59 }
60
61 fn exists(&self, path: &Path) -> bool {
62 path.exists()
63 }
64
65 fn metadata(&self, path: &Path) -> crate::Result<Metadata> {
66 fs::metadata(path).map_err(|err| CoreError::io(path, err))
67 }
68
69 fn symlink_metadata(&self, path: &Path) -> crate::Result<Metadata> {
70 fs::symlink_metadata(path).map_err(|err| CoreError::io(path, err))
71 }
72
73 fn create_dir_all(&self, path: &Path) -> crate::Result<()> {
74 fs::create_dir_all(path).map_err(|err| CoreError::io(path, err))
75 }
76
77 fn create_dir(&self, path: &Path) -> crate::Result<()> {
78 fs::create_dir(path).map_err(|err| CoreError::io(path, err))
79 }
80
81 fn write(&self, path: &Path, data: &[u8]) -> crate::Result<()> {
82 fs::write(path, data).map_err(|err| CoreError::io(path, err))
83 }
84
85 fn write_to_string(&self, path: &Path, content: &str) -> crate::Result<()> {
86 fs::write(path, content).map_err(|err| CoreError::io(path, err))
87 }
88
89 fn read_to_string(&self, path: &Path) -> crate::Result<String> {
90 fs::read_to_string(path).map_err(|err| CoreError::io(path, err))
91 }
92
93 fn remove_file(&self, path: &Path) -> crate::Result<()> {
94 fs::remove_file(path).map_err(|err| CoreError::io(path, err))
95 }
96
97 fn rename(&self, from: &Path, to: &Path) -> crate::Result<()> {
98 fs::rename(from, to).map_err(|err| CoreError::io(from, err))
99 }
100
101 fn list_dir(&self, path: &Path) -> crate::Result<Vec<PathBuf>> {
102 fs::read_dir(path)
103 .map_err(|err| CoreError::io(path, err))?
104 .map(|entry| entry.map(|v| v.path()))
105 .collect::<Result<Vec<PathBuf>, io::Error>>()
106 .map_err(|err| CoreError::io(path, err))
107 }
108
109 fn remove_dir(&self, path: &Path) -> crate::Result<()> {
110 fs::remove_dir(path).map_err(|err| CoreError::io(path, err))
111 }
112}
113
114pub fn first_entry_file_name(entries: &[DirEntry]) -> Option<String> {
117 entries.first().and_then(|entry| {
118 entry
119 .file_name()
120 .to_str()
121 .map(|name| name.to_ascii_lowercase())
122 })
123}