file_access/lib.rs
1//! This crate is a collection of utilities to make performing certain
2//! file manipulations more convenient.
3//!
4//! # Examples
5//! ```
6//! use file_access::AsFile;
7//!
8//! fn main() -> std::io::Result<()> {
9//! Ok({
10//! let text = "Cargo.toml".as_file().read_string()?;
11//! println!("{}", text);
12//!
13//! "Cargo.toml".as_file().read_lines()?
14//! .iter()
15//! .for_each(|line| {
16//! println!("{}", line);
17//! });
18//!
19//! "file.1".as_file().write_string(&"Hello, World!")?;
20//!
21//! let file = "file.1".as_file();
22//! file.append_lines(&vec!["hello", "world"])?;
23//! file.copy_to(&"file.2")?; // copies ./file.1 to ./file.2
24//!
25//! "file.2".as_file().rename_to(&"file.1")?; // replace
26//! "file.1".as_file().delete()?; // clean-up
27//! })
28//! }
29//! ```
30
31pub use as_file::*; // re-export AsFile
32pub use file_path::*; // re-export FilePath
33use internal::{traits::to_vec_string::*, types::*};
34use std::{
35 fs::{self, File, Metadata},
36 io::{Error, ErrorKind, Read, Result},
37 path::PathBuf,
38};
39
40pub mod as_file;
41pub mod file_path;
42mod internal;
43
44// Gets a File::open handle from AsRef<str> such as String or &str
45fn get_file<Path: AsRef<str>>(file_path: &Path) -> Result<File> {
46 File::open(file_path.as_ref())
47}
48
49// Converts AsRef<str> such as String or &str to PathBuf
50fn path_of<Path: AsRef<str>>(file_path: &Path) -> PathBuf {
51 PathBuf::from(file_path.as_ref())
52}
53
54// Creates a file and its full directory path if they don't exist
55fn mk_file<Path: AsRef<str>>(file_path: &Path) -> Result<File> {
56 if let Some(path) = path_of(file_path).parent() {
57 fs::create_dir_all(path)?;
58 }
59 return File::create(file_path.as_ref());
60}
61
62/// Reads the contents of a file.
63///
64/// # Returns
65/// Result<`String`>
66///
67/// # Examples
68/// ```
69/// fn main() -> std::io::Result<()> {
70/// Ok({
71/// let file_path: &str = "Cargo.toml";
72/// let file_path: String = String::from(file_path);
73///
74/// let text: String = file_access::read_string(&file_path)?;
75/// println!("{}", text);
76/// })
77/// }
78/// ```
79pub fn read_string<Path: AsRef<str>>(file_path: &Path) -> Result<String> {
80 let mut buf = String::new();
81 get_file(file_path)?.read_to_string(&mut buf)?;
82
83 return Ok(buf);
84}
85
86/// Reads the contents of a file and returns it as lines.
87///
88/// # Returns
89/// Result<`Vec<String>`>
90///
91/// # Examples
92/// ```
93/// fn main() -> std::io::Result<()> {
94/// Ok({
95/// let file_path: &str = "Cargo.toml";
96/// let file_path: String = String::from(file_path);
97///
98/// let lines: Vec<String> = file_access::read_lines(&file_path)?;
99/// lines.iter().for_each(|line| println!("{}", line));
100/// })
101/// }
102/// ```
103pub fn read_lines<Path: AsRef<str>>(file_path: &Path) -> Result<Lines> {
104 Ok(read_string(file_path)?
105 .lines()
106 .map(ToString::to_string)
107 .collect())
108}
109
110/// Writes text to a file. This function will create the file **and its full directory path** if they don't exist,
111/// and will entirely replace the contents.
112///
113/// # Parameters
114/// - `file_path`: **borrowed** `AsRef<str>` such as `String` or `&str`
115/// - `text`: **borrowed** `AsRef<str>` such as `String` or `&str`
116///
117/// # Returns
118/// Result<`()`>
119///
120/// # Examples
121/// ```
122/// fn main() -> std::io::Result<()> {
123/// Ok({
124/// let file_path: &str = "write_to/absolute_or_relative.path";
125/// let file_path: String = String::from(file_path);
126///
127/// let text: &str = "Hello, World!";
128/// let text: String = String::from(text);
129///
130/// file_access::write_string(&file_path, &text)?;
131///
132/// // Clean-up:
133/// file_access::delete(&"write_to")?; // ./write_to/
134/// })
135/// }
136/// ```
137pub fn write_string<Path: AsRef<str>, Text: AsRef<str>>(
138 file_path: &Path,
139 text: &Text,
140) -> Result<()> {
141 let path = path_of(file_path);
142 if !path.exists() {
143 mk_file(file_path)?;
144 }
145 return fs::write(path, text.as_ref());
146}
147
148/// Writes a list of text as lines to a file. This function will create the file **and its full directory path** if they don't exist,
149/// and will entirely replace the contents with the provided strings each on its own line.
150///
151/// # Parameters
152/// - `file_path`: **borrowed** `AsRef<str>` such as `String` or `&str`
153/// - `lines`: **borrowed** `Vec<AsRef<str>>` such as `Vec<String>` or `Vec<&str>`
154///
155/// # Returns
156/// Result<`()`>
157///
158/// # Examples
159/// ```
160/// fn main() -> std::io::Result<()> {
161/// Ok({
162/// let file_path: &str = "lines_to/absolute_or_relative.path";
163/// let file_path: String = String::from(file_path);
164///
165/// let lines: Vec<&str> = "Hello, World!".split_whitespace().collect();
166/// let lines: Vec<String> = lines.iter().map(ToString::to_string).collect();
167///
168/// file_access::write_lines(&file_path, &lines)?;
169///
170/// // Clean-up:
171/// file_access::delete(&"lines_to"); // ./lines_to/
172/// })
173/// }
174/// ```
175pub fn write_lines<Path: AsRef<str>, Line: AsRef<str>>(
176 file_path: &Path,
177 lines: &Vec<Line>,
178) -> Result<()> {
179 write_string(file_path, &lines.to_vec_string().join("\n"))
180}
181
182/// Appends text to a file. This function will append the contents of the file,
183/// or write a new one **and its full directory path** if they don't exist yet.
184///
185/// # Parameters
186/// - `file_path`: **borrowed** `AsRef<str>` such as `String` or `&str`
187/// - `text`: **borrowed** `AsRef<str>` such as `String` or `&str`
188///
189/// # Returns
190/// Result<`()`>
191///
192/// # Examples
193/// ```
194/// fn main() -> std::io::Result<()> {
195/// Ok({
196/// let file_path: &str = "append_to/absolute_or_relative.path";
197/// let file_path: String = String::from(file_path);
198///
199/// let text: &str = "Hello, World!";
200/// let text: String = String::from(text);
201///
202/// file_access::append_string(&file_path, &text)?;
203///
204/// // Clean-up:
205/// file_access::delete(&"append_to"); // ./append_to/
206/// })
207/// }
208/// ```
209pub fn append_string<Path: AsRef<str>, Text: AsRef<str>>(
210 file_path: &Path,
211 text: &Text,
212) -> Result<()> {
213 write_string(
214 file_path,
215 &match read_string(file_path) {
216 Ok(file) => format!("{}{}", file, text.as_ref()),
217 Err(_) => text.as_ref().to_string(),
218 },
219 )
220}
221
222/// Appends a list of text as lines to a file. This function will append the contents of the file,
223/// or write a new one **and its full directory path** if they don't exist yet.
224///
225/// # Parameters
226/// - `file_path`: **borrowed** `AsRef<str>` such as `String` or `&str`
227/// - `lines`: **borrowed** `Vec<AsRef<str>>` such as `Vec<String>` or `Vec<&str>`
228///
229/// # Returns
230/// Result<`()`>
231///
232/// # Examples
233/// ```
234/// fn main() -> std::io::Result<()> {
235/// Ok({
236/// let file_path: &str = "append_lines_to/absolute_or_relative.path";
237/// let file_path: String = String::from(file_path);
238///
239/// let lines: Vec<&str> = "Hello, World!".split_whitespace().collect();
240/// let lines: Vec<String> = lines.iter().map(ToString::to_string).collect();
241///
242/// file_access::append_lines(&file_path, &lines)?;
243///
244/// // Clean-up:
245/// file_access::delete(&"append_lines_to"); // ./append_lines_to/
246/// })
247/// }
248/// ```
249pub fn append_lines<Path: AsRef<str>, Line: AsRef<str>>(
250 file_path: &Path,
251 lines: &Vec<Line>,
252) -> Result<()> {
253 let mut file = match read_lines(file_path) {
254 Ok(lines) => lines,
255 Err(_) => vec![],
256 };
257 file.extend_from_slice(&lines.to_vec_string());
258
259 return write_lines(file_path, &file);
260}
261
262/// Deletes a file, or a directory **recursively**.
263///
264/// # Parameters
265/// - `file_path`: **borrowed** `AsRef<str>` such as `String` or `&str`
266///
267/// # Returns
268/// Result<`()`>
269///
270/// # Examples
271/// ```
272/// fn main() -> std::io::Result<()> {
273/// Ok({
274/// let file_path: &str = "absolute_or_relative_path/to_a_file/or_a_directory";
275/// let file_path: String = String::from(file_path);
276///
277/// file_access::write_string(&file_path, &"Hello, World!");
278/// file_access::delete(&file_path)?; // delete file
279///
280/// // Delete directory:
281/// file_access::delete(&"absolute_or_relative_path")?;
282/// })
283/// }
284/// ```
285pub fn delete<Path: AsRef<str>>(file_path: &Path) -> Result<()> {
286 let path = path_of(file_path);
287
288 if path.is_file() {
289 return fs::remove_file(path);
290 }
291
292 if path.is_dir() {
293 return fs::remove_dir_all(path);
294 }
295
296 return Err(Error::new(ErrorKind::InvalidInput, file_path.as_ref()));
297}
298
299/// Copies the contents of a file and write it to a destination.
300/// This function will entirely replace the contents of the destination if it already exists.
301///
302/// # Parameters
303/// - `from`: **borrowed** `AsRef<str>` such as `String` or `&str`
304/// - `to`: **borrowed** `AsRef<str>` such as `String` or `&str`
305///
306/// # Returns
307/// Result<`()`>
308///
309/// # Examples
310/// ```
311/// fn main() -> std::io::Result<()> {
312/// Ok({
313/// let source: &str = "Cargo.toml";
314/// let source: String = String::from(source);
315///
316/// let destination: &str = "Cargo.toml.2";
317/// let destination: String = String::from(destination);
318///
319/// file_access::copy(&source, &destination)?;
320///
321/// // Delete file:
322/// file_access::delete(&destination);
323/// })
324/// }
325/// ```
326pub fn copy<From: AsRef<str>, To: AsRef<str>>(from: &From, to: &To) -> Result<()> {
327 write_string(to, &read_string(from)?)
328}
329
330/// Copies the contents of a file, writes it to a destination and then deletes the source.
331/// This function will entirely replace the contents of the destination if it already exists.
332///
333/// # Parameters
334/// - `from`: **borrowed** `AsRef<str>` such as `String` or `&str`
335/// - `to`: **borrowed** `AsRef<str>` such as `String` or `&str`
336///
337/// # Returns
338/// Result<`()`>
339///
340/// # Examples
341/// ```
342/// fn main() -> std::io::Result<()> {
343/// Ok({
344/// let source: &str = "file.1";
345/// let source: String = String::from(source);
346///
347/// let destination: &str = "file.2";
348/// let destination: String = String::from(destination);
349///
350/// file_access::write_string(&source, &"Hello, World!")?;
351/// file_access::rename(&source, &destination)?;
352///
353/// // Clean-up:
354/// file_access::delete(&destination)?;
355/// })
356/// }
357/// ```
358pub fn rename<From: AsRef<str>, To: AsRef<str>>(from: &From, to: &To) -> Result<()> {
359 copy(from, to)?;
360
361 return delete(from);
362}
363
364/// Queries metadata about the underlying file.
365///
366/// # Returns
367/// Result<`Metadata`>
368///
369/// # Examples
370/// ```
371/// fn main() -> std::io::Result<()> {
372/// Ok({
373/// let file_path: &str = "Cargo.toml";
374/// let file_path: String = String::from(file_path);
375///
376/// let metadata: std::fs::Metadata = file_access::get_metadata(&file_path)?;
377/// println!("{:#?}", metadata);
378/// })
379/// }
380/// ```
381pub fn get_metadata<Path: AsRef<str>>(file_path: &Path) -> Result<Metadata> {
382 get_file(file_path)?.metadata()
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use std::io::Result;
389
390 // cargo test -- --show-output --test-threads=1
391 // cargo test <TESTNAME> --show-output
392
393 #[test]
394 fn read_string() -> Result<()> {
395 Ok({
396 // Arrange
397 let file = "Cargo.toml";
398
399 // Action
400 let text = super::read_string(&file)?;
401 println!("{text}");
402
403 // Assert
404 assert_ne!(text.len(), 0);
405 })
406 }
407
408 #[test]
409 fn read_lines() -> Result<()> {
410 Ok({
411 // Arrange
412 let file = "Cargo.toml";
413
414 // Action
415 let lines = super::read_lines(&file)?;
416 for line in &lines {
417 println!("{line}");
418 }
419
420 // Assert
421 assert_ne!(lines.len(), 0);
422 })
423 }
424
425 #[test]
426 fn write_string() -> Result<()> {
427 Ok({
428 // Arrange
429 let file = "write_string/file_access.txt";
430 let text = "Hello, World!";
431
432 // Action
433 super::write_string(&file, &text)?;
434
435 // Assert
436 assert_eq!(super::read_string(&file)?, text);
437
438 // Clean-up
439 super::delete(&"write_string")?;
440 })
441 }
442
443 #[test]
444 fn write_lines() -> Result<()> {
445 Ok({
446 // Arrange
447 let file = "write_lines/file_access.txt";
448 let lines = "Hello, World!"
449 .split_whitespace()
450 .map(ToString::to_string)
451 .collect();
452
453 // Action
454 super::write_lines(&file, &lines)?;
455
456 // Assert
457 assert_eq!(super::read_lines(&file)?, lines);
458
459 // Clean-up
460 super::delete(&"write_lines")?;
461 })
462 }
463
464 #[test]
465 fn append_string() -> Result<()> {
466 Ok({
467 // Arrange
468 let file = "append_string/file_access.txt";
469 let text = "Hello, World!";
470 super::write_string(&file, &text)?;
471
472 // Action
473 super::append_string(&file, &text)?;
474
475 // Assert
476 assert_eq!(super::read_string(&file)?, format!("{text}{text}"));
477
478 // Clean-up
479 super::delete(&"append_string")?;
480 })
481 }
482
483 #[test]
484 fn append_lines() -> Result<()> {
485 Ok({
486 // Arrange
487 let file = "append_lines/file_access.txt";
488 let lines1 = vec!["1", "2"]; // .to_vec_string();
489 super::write_lines(&file, &lines1)?;
490
491 // Action
492 let lines2 = vec!["3", "4"]; //.to_vec_string();
493 super::append_lines(&file, &lines2)?;
494
495 // Assert
496 assert_eq!(super::read_lines(&file)?, vec!["1", "2", "3", "4"]); // .to_vec_string());
497
498 // Clean-up
499 super::delete(&"append_lines")?;
500 })
501 }
502
503 #[test]
504 fn delete() -> Result<()> {
505 Ok({
506 // Arrange
507 let file = "delete/file_access.txt";
508 mk_file(&file)?;
509
510 // Action
511 super::delete(&file)?;
512
513 // Assert
514 assert!(!path_of(&file).exists(), "{file} should no longer exist");
515
516 // Clean-up
517 super::delete(&"delete")?;
518 })
519 }
520
521 #[test]
522 fn copy() -> Result<()> {
523 Ok({
524 // Arrange
525 let from = "copy_from/file_access.txt";
526 let to = "copy_to/file_access.txt";
527 super::write_string(&from, &"Hello, World!")?;
528
529 // Action
530 super::copy(&from, &to)?;
531
532 // Assert
533 assert_eq!(
534 super::read_string(&from)?,
535 super::read_string(&to)?,
536 "{from} and {to} should contain the same text"
537 );
538
539 // Clean-up
540 super::delete(&"copy_from")?;
541 super::delete(&"copy_to")?;
542 })
543 }
544
545 #[test]
546 fn rename() -> Result<()> {
547 Ok({
548 // Arrange
549 let from = "rename_from/file_access.txt";
550 let to = "rename_to/file_access.txt";
551 let text = "Hello, World!";
552 super::write_string(&from, &text)?;
553
554 // Action
555 super::rename(&from, &to)?;
556
557 // Assert
558 assert!(!path_of(&from).exists(), "{from} should no longer exist");
559 assert_eq!(
560 super::read_string(&to)?,
561 text,
562 "{to} should contain: {text}"
563 );
564
565 // Clean-up
566 super::delete(&"rename_from")?;
567 super::delete(&"rename_to")?;
568 })
569 }
570}