fsio/file.rs
1//! # file
2//!
3//! File utility functions.
4//!
5
6#[cfg(test)]
7#[path = "./file_test.rs"]
8mod file_test;
9
10use crate::directory;
11use crate::error::FsIOError;
12use crate::path::as_path::AsPath;
13use crate::types::FsIOResult;
14use std::fs::{read, read_to_string, remove_file, File, OpenOptions};
15use std::io;
16use std::io::Write;
17
18/// Ensures the provided path leads to an existing file.
19/// If the file does not exist, this function will create an emtpy file.
20///
21/// # Arguments
22///
23/// * `path` - The file path
24///
25/// # Example
26///
27/// ```
28/// use crate::fsio::file;
29/// use std::path::Path;
30///
31/// fn main() {
32/// let result = file::ensure_exists("./target/__test/file_test/dir1/dir2/file.txt");
33/// assert!(result.is_ok());
34///
35/// let path = Path::new("./target/__test/file_test/dir1/dir2/file.txt");
36/// assert!(path.exists());
37/// }
38/// ```
39pub fn ensure_exists<T: AsPath + ?Sized>(path: &T) -> FsIOResult<()> {
40 let file_path = path.as_path();
41
42 if file_path.exists() {
43 if file_path.is_file() {
44 Ok(())
45 } else {
46 Err(FsIOError::PathAlreadyExists(
47 format!("Unable to create file: {:?}", &file_path).to_string(),
48 ))
49 }
50 } else {
51 directory::create_parent(path)?;
52
53 match File::create(&file_path) {
54 Ok(_) => Ok(()),
55 Err(error) => Err(FsIOError::IOError(
56 format!("Unable to create file: {:?}", &file_path).to_string(),
57 Some(error),
58 )),
59 }
60 }
61}
62
63/// Creates and writes the text to the requested file path.
64/// If a file exists at that path, it will be overwritten.
65///
66/// # Arguments
67///
68/// * `path` - The file path
69/// * `text` - The file text content
70///
71/// # Example
72///
73/// ```
74/// use crate::fsio::file;
75/// use std::path::Path;
76///
77/// fn main() {
78/// let file_path = "./target/__test/file_test/write_text_file/file.txt";
79/// let result = file::write_text_file(file_path, "some content");
80/// assert!(result.is_ok());
81///
82/// let text = file::read_text_file(file_path).unwrap();
83///
84/// assert_eq!(text, "some content");
85/// }
86/// ```
87pub fn write_text_file<T: AsPath + ?Sized>(path: &T, text: &str) -> FsIOResult<()> {
88 write_file(path, text.as_bytes())
89}
90
91/// Appends (or creates) and writes the text to the requested file path.
92/// If a file exists at that path, the content will be appended.
93///
94/// # Arguments
95///
96/// * `path` - The file path
97/// * `text` - The file text content
98///
99/// # Example
100///
101/// ```
102/// use crate::fsio::file;
103/// use std::path::Path;
104///
105/// fn main() {
106/// let file_path = "./target/__test/file_test/append_text_file/file.txt";
107/// let mut result = file::write_text_file(file_path, "some content");
108/// assert!(result.is_ok());
109/// result = file::append_text_file(file_path, "\nmore content");
110/// assert!(result.is_ok());
111///
112/// let text = file::read_text_file(file_path).unwrap();
113///
114/// assert_eq!(text, "some content\nmore content");
115/// }
116/// ```
117pub fn append_text_file<T: AsPath + ?Sized>(path: &T, text: &str) -> FsIOResult<()> {
118 append_file(path, text.as_bytes())
119}
120
121/// Creates and writes the raw data to the requested file path.
122/// If a file exists at that path, it will be overwritten.
123///
124/// # Arguments
125///
126/// * `path` - The file path
127/// * `data` - The file raw content
128///
129/// # Example
130///
131/// ```
132/// use crate::fsio::file;
133/// use std::path::Path;
134/// use std::str;
135///
136/// fn main() {
137/// let file_path = "./target/__test/file_test/write_file/file.txt";
138/// let mut result = file::write_file(file_path, "some content".as_bytes());
139/// assert!(result.is_ok());
140/// result = file::append_file(file_path, "\nmore content".as_bytes());
141/// assert!(result.is_ok());
142///
143/// let data = file::read_file(file_path).unwrap();
144///
145/// assert_eq!(str::from_utf8(&data).unwrap(), "some content\nmore content");
146/// }
147/// ```
148pub fn write_file<T: AsPath + ?Sized>(path: &T, data: &[u8]) -> FsIOResult<()> {
149 modify_file(path, &move |file: &mut File| file.write_all(data), false)
150}
151
152/// Appends (or creates) and writes the raw data to the requested file path.
153/// If a file exists at that path, the content will be appended.
154///
155/// # Arguments
156///
157/// * `path` - The file path
158/// * `data` - The file raw content
159///
160/// # Example
161///
162/// ```
163/// use crate::fsio::file;
164/// use std::path::Path;
165/// use std::str;
166///
167/// fn main() {
168/// let file_path = "./target/__test/file_test/append_file/file.txt";
169/// let mut result = file::write_file(file_path, "some content".as_bytes());
170/// assert!(result.is_ok());
171/// result = file::append_file(file_path, "\nmore content".as_bytes());
172/// assert!(result.is_ok());
173///
174/// let data = file::read_file(file_path).unwrap();
175///
176/// assert_eq!(str::from_utf8(&data).unwrap(), "some content\nmore content");
177/// }
178/// ```
179pub fn append_file<T: AsPath + ?Sized>(path: &T, data: &[u8]) -> FsIOResult<()> {
180 modify_file(path, &move |file: &mut File| file.write_all(data), true)
181}
182
183/// Overwrites or appends the requested file and triggers the provided write_content function to
184/// enable custom writing.
185///
186/// # Arguments
187///
188/// * `path` - The file path
189/// * `write_content` - The custom writing function
190/// * `append` - True to append false to overwrite
191///
192/// # Example
193///
194/// ```
195/// use crate::fsio::file;
196/// use std::fs::File;
197/// use std::io::Write;
198/// use std::str;
199///
200/// fn main() {
201/// let file_path = "./target/__test/file_test/modify_file/file.txt";
202/// let mut result = file::modify_file(
203/// file_path,
204/// &move |file: &mut File| file.write_all("some content".as_bytes()),
205/// false,
206/// );
207/// assert!(result.is_ok());
208/// result = file::modify_file(
209/// file_path,
210/// &move |file: &mut File| file.write_all("\nmore content".as_bytes()),
211/// true,
212/// );
213/// assert!(result.is_ok());
214///
215/// let data = file::read_file(file_path).unwrap();
216///
217/// assert_eq!(str::from_utf8(&data).unwrap(), "some content\nmore content");
218/// }
219/// ```
220pub fn modify_file<T: AsPath + ?Sized>(
221 path: &T,
222 write_content: &dyn Fn(&mut File) -> io::Result<()>,
223 append: bool,
224) -> FsIOResult<()> {
225 directory::create_parent(path)?;
226
227 let file_path = path.as_path();
228
229 // create or open
230 let result = if append && file_path.exists() {
231 OpenOptions::new().append(true).open(file_path)
232 } else {
233 File::create(&file_path)
234 };
235
236 match result {
237 Ok(mut fd) => match write_content(&mut fd) {
238 Ok(_) => match fd.sync_all() {
239 Ok(_) => Ok(()),
240 Err(error) => Err(FsIOError::IOError(
241 format!("Error finish up writing to file: {:?}", &file_path).to_string(),
242 Some(error),
243 )),
244 },
245 Err(error) => Err(FsIOError::IOError(
246 format!("Error while writing to file: {:?}", &file_path).to_string(),
247 Some(error),
248 )),
249 },
250 Err(error) => Err(FsIOError::IOError(
251 format!("Unable to create/open file: {:?} for writing.", &file_path).to_string(),
252 Some(error),
253 )),
254 }
255}
256
257/// Reads the requested text file and returns its content.
258///
259/// # Arguments
260///
261/// * `path` - The file path
262///
263/// # Example
264///
265/// ```
266/// use crate::fsio::file;
267/// use std::path::Path;
268///
269/// fn main() {
270/// let file_path = "./target/__test/file_test/write_text_file/file.txt";
271/// let result = file::write_text_file(file_path, "some content");
272/// assert!(result.is_ok());
273///
274/// let text = file::read_text_file(file_path).unwrap();
275///
276/// assert_eq!(text, "some content");
277/// }
278/// ```
279pub fn read_text_file<T: AsPath + ?Sized>(path: &T) -> FsIOResult<String> {
280 let file_path = path.as_path();
281
282 match read_to_string(&file_path) {
283 Ok(content) => Ok(content),
284 Err(error) => Err(FsIOError::IOError(
285 format!("Unable to read file: {:?}", &file_path).to_string(),
286 Some(error),
287 )),
288 }
289}
290
291/// Reads the requested file and returns its content.
292///
293/// # Arguments
294///
295/// * `path` - The file path
296///
297/// # Example
298///
299/// ```
300/// use crate::fsio::file;
301/// use std::path::Path;
302/// use std::str;
303///
304/// fn main() {
305/// let file_path = "./target/__test/file_test/read_file/file.txt";
306/// let mut result = file::write_file(file_path, "some content".as_bytes());
307/// assert!(result.is_ok());
308/// result = file::append_file(file_path, "\nmore content".as_bytes());
309/// assert!(result.is_ok());
310///
311/// let data = file::read_file(file_path).unwrap();
312///
313/// assert_eq!(str::from_utf8(&data).unwrap(), "some content\nmore content");
314/// }
315/// ```
316pub fn read_file<T: AsPath + ?Sized>(path: &T) -> FsIOResult<Vec<u8>> {
317 let file_path = path.as_path();
318
319 match read(&file_path) {
320 Ok(content) => Ok(content),
321 Err(error) => Err(FsIOError::IOError(
322 format!("Unable to read file: {:?}", &file_path).to_string(),
323 Some(error),
324 )),
325 }
326}
327
328/// Deletes the requested file.
329/// If the file does not exist, this function will return valid response.
330///
331/// # Arguments
332///
333/// * `path` - The file path
334///
335/// # Example
336///
337/// ```
338/// use crate::fsio::file;
339/// use std::path::Path;
340/// use std::str;
341///
342/// fn main() {
343/// let file_path = "./target/__test/file_test/delete_file/file.txt";
344/// let mut result = file::ensure_exists(file_path);
345/// assert!(result.is_ok());
346///
347/// let path = Path::new(file_path);
348/// assert!(path.exists());
349///
350/// result = file::delete(file_path);
351/// assert!(result.is_ok());
352///
353/// assert!(!path.exists());
354/// }
355/// ```
356pub fn delete<T: AsPath + ?Sized>(path: &T) -> FsIOResult<()> {
357 let file_path = path.as_path();
358
359 if file_path.exists() {
360 if file_path.is_file() {
361 match remove_file(file_path) {
362 Ok(_) => Ok(()),
363 Err(error) => Err(FsIOError::IOError(
364 format!("Unable to delete file: {:?}", &file_path).to_string(),
365 Some(error),
366 )),
367 }
368 } else {
369 Err(FsIOError::NotFile(
370 format!("Path: {:?} is not a file.", &file_path).to_string(),
371 ))
372 }
373 } else {
374 Ok(())
375 }
376}
377
378/// Deletes the requested file.
379/// If the file does not exist, this function will return true.
380///
381/// # Arguments
382///
383/// * `path` - The file path
384///
385/// # Example
386///
387/// ```
388/// use crate::fsio::file;
389/// use std::path::Path;
390/// use std::str;
391///
392/// fn main() {
393/// let file_path = "./target/__test/file_test/delete_file/file.txt";
394/// let result = file::ensure_exists(file_path);
395/// assert!(result.is_ok());
396///
397/// let path = Path::new(file_path);
398/// assert!(path.exists());
399///
400/// let deleted = file::delete_ignore_error(file_path);
401/// assert!(deleted);
402///
403/// assert!(!path.exists());
404/// }
405/// ```
406pub fn delete_ignore_error<T: AsPath + ?Sized>(path: &T) -> bool {
407 match delete(path) {
408 Ok(_) => true,
409 Err(_) => false,
410 }
411}