aperture_cli/fs.rs
1use std::io;
2use std::path::{Path, PathBuf};
3
4pub trait FileSystem {
5 /// Reads the entire contents of a file into a string.
6 ///
7 /// # Errors
8 ///
9 /// Returns an error if the file does not exist, cannot be read, or contains invalid UTF-8.
10 fn read_to_string(&self, path: &Path) -> io::Result<String>;
11
12 /// Writes a slice of bytes to a file, creating the file if it does not exist.
13 ///
14 /// # Errors
15 ///
16 /// Returns an error if the file cannot be written to or created.
17 fn write_all(&self, path: &Path, contents: &[u8]) -> io::Result<()>;
18
19 /// Creates a directory and all of its parent components if they are missing.
20 ///
21 /// # Errors
22 ///
23 /// Returns an error if the directory cannot be created.
24 fn create_dir_all(&self, path: &Path) -> io::Result<()>;
25
26 /// Removes a file from the filesystem.
27 ///
28 /// # Errors
29 ///
30 /// Returns an error if the file does not exist or cannot be removed.
31 fn remove_file(&self, path: &Path) -> io::Result<()>;
32
33 /// Removes a directory and all of its contents.
34 ///
35 /// # Errors
36 ///
37 /// Returns an error if the directory does not exist or cannot be removed.
38 fn remove_dir_all(&self, path: &Path) -> io::Result<()>;
39
40 /// Returns `true` if the path points to an existing entity.
41 fn exists(&self, path: &Path) -> bool;
42
43 /// Returns `true` if the path exists and is pointing at a directory.
44 fn is_dir(&self, path: &Path) -> bool;
45
46 /// Returns `true` if the path exists and is pointing at a regular file.
47 fn is_file(&self, path: &Path) -> bool;
48
49 /// Returns the canonical, absolute form of the path with all intermediate components normalized.
50 ///
51 /// # Errors
52 ///
53 /// Returns an error if the path does not exist or cannot be canonicalized.
54 fn canonicalize(&self, path: &Path) -> io::Result<PathBuf>;
55
56 /// Returns a vector of all entries in a directory.
57 ///
58 /// # Errors
59 ///
60 /// Returns an error if the directory does not exist or cannot be read.
61 fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>>;
62
63 /// Writes data to a file atomically using a temp-file + rename strategy.
64 ///
65 /// The default implementation delegates to [`crate::atomic::atomic_write_sync`].
66 /// Test doubles may override this to use a simpler (non-atomic) write.
67 ///
68 /// # Errors
69 ///
70 /// Returns an error if the write or rename fails.
71 fn atomic_write(&self, path: &Path, contents: &[u8]) -> io::Result<()> {
72 crate::atomic::atomic_write_sync(path, contents)
73 }
74}
75
76pub struct OsFileSystem;
77
78impl FileSystem for OsFileSystem {
79 fn read_to_string(&self, path: &Path) -> io::Result<String> {
80 std::fs::read_to_string(path)
81 }
82
83 fn write_all(&self, path: &Path, contents: &[u8]) -> io::Result<()> {
84 std::fs::write(path, contents)
85 }
86
87 fn create_dir_all(&self, path: &Path) -> io::Result<()> {
88 std::fs::create_dir_all(path)
89 }
90
91 fn remove_file(&self, path: &Path) -> io::Result<()> {
92 std::fs::remove_file(path)
93 }
94
95 fn remove_dir_all(&self, path: &Path) -> io::Result<()> {
96 std::fs::remove_dir_all(path)
97 }
98
99 fn exists(&self, path: &Path) -> bool {
100 path.exists()
101 }
102
103 fn is_dir(&self, path: &Path) -> bool {
104 path.is_dir()
105 }
106
107 fn is_file(&self, path: &Path) -> bool {
108 path.is_file()
109 }
110
111 fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {
112 path.canonicalize()
113 }
114
115 fn read_dir(&self, path: &Path) -> io::Result<Vec<PathBuf>> {
116 Ok(std::fs::read_dir(path)?
117 .filter_map(std::result::Result::ok)
118 .map(|entry| entry.path())
119 .collect())
120 }
121}