dev_tool/
file_util.rs

1//! 文件工具
2use std::fs::{self, File};
3use std::io::{self, Read, Write};
4use std::path::Path;
5
6/// 文件工具结构体
7pub struct FileUtil;
8
9impl FileUtil {
10    /// 罗列指定路径下的子目录及文件。
11    ///
12    /// 参数 `path` 是要罗列内容的目标路径。
13    /// 参数 `recurse` 决定是否以递归方式罗列子文件夹内的内容。
14    ///
15    /// 返回值是一个包含所有子目录及文件路径的 `Vec<String>`。
16    /// 如果在读取目录时发生错误,将返回一个空的 `Vec`。
17    pub fn list(path: &Path, recurse: bool) -> Vec<String> {
18        let mut result = Vec::new();
19        let entries = match fs::read_dir(path) {
20            Ok(entries) => entries,
21            Err(_) => return result,
22        };
23
24        for entry in entries {
25            let entry = match entry {
26                Ok(entry) => entry,
27                Err(_) => continue,
28            };
29            let path = entry.path();
30            if path.is_dir() {
31                if recurse {
32                    result.extend(Self::list(&path, true));
33                }
34                result.push(path.to_string_lossy().to_string());
35            } else {
36                result.push(path.to_string_lossy().to_string());
37            }
38        }
39        result
40    }
41
42    /// 获取指定文件的元数据信息
43    ///
44    /// 该函数封装了标准库中的 fs::metadata 方法,用于获取文件的基本信息,
45    /// 包括文件大小、创建时间、修改时间、文件类型等元数据。
46    ///
47    /// # 参数
48    /// * `file_path` - 要获取元数据的文件路径字符串引用
49    ///
50    /// # 返回值
51    /// 返回一个 io::Result<std::fs::Metadata> 类型:
52    /// * 成功时返回包含文件元数据的 Metadata 对象
53    /// * 失败时返回对应的 IO 错误信息
54    ///
55    /// # 错误处理
56    /// 当文件不存在或没有访问权限时,会返回相应的 IO 错误
57    pub fn metadata(file_path: &str) -> io::Result<std::fs::Metadata> {
58        fs::metadata(file_path)
59    }
60
61    /// 读取本地文件内容并以UTF - 8字符串形式返回。
62    ///
63    /// 参数 `path` 是要读取的文件路径。
64    ///
65    /// 如果文件成功打开并读取,将返回包含文件内容的 `String`。
66    /// 如果在打开或读取文件时发生I/O错误,将返回对应的 `io::Error`。
67    pub fn read_string(path: &Path) -> Result<String, io::Error> {
68        let mut file = File::open(path)?;
69        let mut contents = String::new();
70        file.read_to_string(&mut contents)?;
71        Ok(contents)
72    }
73
74    /// 读取本地文件内容并以字节数组形式返回。
75    ///
76    /// 参数 `path` 是要读取的文件路径。
77    ///
78    /// 如果文件成功打开并读取,将返回包含文件内容的 `Vec<u8>`。
79    /// 如果在打开或读取文件时发生I/O错误,将返回对应的 `io::Error`。
80    pub fn read_bytes(path: &Path) -> Result<Vec<u8>, io::Error> {
81        let mut file = File::open(path)?;
82        let mut contents = Vec::new();
83        file.read_to_end(&mut contents)?;
84        Ok(contents)
85    }
86
87    /// 将给定的字符串内容以覆盖方式写入到指定路径的文本文件。
88    ///
89    /// 参数 `path` 是要写入的文件路径。
90    /// 参数 `content` 是要写入文件的字符串内容。
91    ///
92    /// 如果文件成功创建并写入内容,将返回 `Ok(())`。
93    /// 如果在创建或写入文件时发生I/O错误,将返回对应的 `io::Error`。
94    pub fn write_string(path: &Path, content: String) -> io::Result<()> {
95        let mut file = File::create(path)?;
96        file.write_all(content.as_bytes())?;
97        Ok(())
98    }
99
100    /// 将给定的字符串内容以追加方式写入到指定路径的文件。
101    ///
102    /// 参数 `path` 是要写入的文件路径。
103    /// 参数 `content` 是要追加到文件的字符串内容。
104    ///
105    /// 如果文件成功打开并追加内容,将返回 `Ok(())`。
106    /// 如果在打开或写入文件时发生I/O错误,将返回对应的 `io::Error`。
107    pub fn append_string(path: &Path, content: String) -> io::Result<()> {
108        let mut file = File::options().append(true).open(path)?;
109        file.write_all(content.as_bytes())?;
110        Ok(())
111    }
112
113    /// 将给定的字节数组内容写入到指定路径的文件。
114    ///
115    /// 参数 `path` 是要写入的文件路径。
116    /// 参数 `bytes` 是要写入文件的字节数组内容。
117    ///
118    /// 如果文件成功创建并写入内容,将返回 `Ok(())`。
119    /// 如果在创建或写入文件时发生I/O错误,将返回对应的 `io::Error`。
120    pub fn write_bytes(path: &Path, bytes: &[u8]) -> io::Result<()> {
121        let mut file = File::create(path)?;
122        file.write_all(bytes)?;
123        Ok(())
124    }
125
126    /// 创建目录及其所有必要的父目录
127    pub fn create_dir_with_parents(dir_path: &str) -> io::Result<()> {
128        fs::create_dir_all(dir_path)
129    }
130
131    /// 删除单个文件
132    pub fn delete_file(file_path: &str) -> io::Result<()> {
133        fs::remove_file(file_path)
134    }
135
136    /// 删除含有子文件的目录
137    pub fn delete_directory(dir_path: &str) -> io::Result<()> {
138        fs::remove_dir_all(dir_path)
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use std::fs::remove_file;
146    use std::path::PathBuf;
147    static root_dir: &str = "d:/tmp/";
148
149    #[test]
150    fn test_list() {
151        let temp_dir = Path::new(root_dir);
152        let sub_dir = temp_dir.join("sub_dir");
153        std::fs::create_dir(&sub_dir).expect("Failed to create sub dir");
154        let file_path = sub_dir.join("test_file.txt");
155        std::fs::File::create(&file_path).expect("Failed to create file");
156
157        let result = FileUtil::list(temp_dir, true);
158        assert!(result.contains(&file_path.to_string_lossy().to_string()));
159    }
160
161    #[test]
162    fn test_read_string() -> io::Result<()> {
163        let temp_path = Path::new(root_dir).join("temp_file.txt");
164        println!("{:?}", temp_path);
165        let mut temp_file = File::options()
166            .write(true)
167            .create(true)
168            .open(temp_path.as_path())?;
169        let content = "test content".to_string();
170        temp_file
171            .write_all(content.as_bytes())
172            .expect("Failed to write to temp file");
173
174        let result = FileUtil::read_string(temp_path.as_path());
175        assert!(result.is_ok());
176        assert_eq!(result.unwrap(), content);
177        Ok(())
178    }
179
180    #[test]
181    fn test_read_bytes() {
182        let temp_path = Path::new(root_dir).join("temp_file.txt");
183        let mut temp_file = File::options()
184            .write(true)
185            .open(temp_path.as_path())
186            .unwrap();
187        let content = [1, 2, 3];
188        temp_file
189            .write_all(&content)
190            .expect("Failed to write to temp file");
191
192        let result = FileUtil::read_bytes(temp_path.as_path());
193        assert!(result.is_ok());
194    }
195
196    #[test]
197    fn test_write_string() {
198        let temp_path = Path::new(root_dir).join("temp_file.txt");
199        let mut temp_file = File::options().write(true).open(&temp_path).unwrap();
200        let content = "test write content".to_string();
201
202        let result = FileUtil::write_string(&temp_path, content.clone());
203        assert!(result.is_ok());
204
205        let read_result = FileUtil::read_string(&temp_path);
206        assert!(read_result.is_ok());
207        assert_eq!(read_result.unwrap(), content);
208    }
209
210    #[test]
211    fn test_append_string() {
212        let temp_path = Path::new(root_dir).join("temp_file.txt");
213        let mut temp_file = File::open(&temp_path).unwrap();
214        let initial_content = "initial content".to_string();
215        let append_content = " appended content".to_string();
216        FileUtil::write_string(&temp_path, initial_content.clone())
217            .expect("Failed to write initial content");
218
219        let append_result = FileUtil::append_string(&temp_path, append_content.clone());
220        assert!(append_result.is_ok());
221
222        let read_result = FileUtil::read_string(&temp_path);
223        assert!(read_result.is_ok());
224        assert_eq!(read_result.unwrap(), initial_content + &append_content);
225    }
226
227    #[test]
228    fn test_write_bytes() {
229        let temp_path = Path::new(root_dir).join("temp_file.txt");
230        let mut temp_file = File::open(&temp_path).unwrap();
231        let content = [4, 5, 6];
232
233        let result = FileUtil::write_bytes(&temp_path, &content);
234        assert!(result.is_ok());
235
236        let read_result = FileUtil::read_bytes(&temp_path);
237        assert!(read_result.is_ok());
238        assert_eq!(read_result.unwrap(), content);
239    }
240}