1 | /*!
|
2 | fs-err is a drop-in replacement for [`std::fs`][std::fs] that provides more
|
3 | helpful messages on errors. Extra information includes which operations was
|
4 | attempted and any involved paths.
|
5 |
|
6 | # Error Messages
|
7 |
|
8 | Using [`std::fs`][std::fs], if this code fails:
|
9 |
|
10 | ```no_run
|
11 | # use std::fs::File;
|
12 | let file = File::open("does not exist.txt" )?;
|
13 | # Ok::<(), std::io::Error>(())
|
14 | ```
|
15 |
|
16 | The error message that Rust gives you isn't very useful:
|
17 |
|
18 | ```txt
|
19 | The system cannot find the file specified. (os error 2)
|
20 | ```
|
21 |
|
22 | ...but if we use fs-err instead, our error contains more actionable information:
|
23 |
|
24 | ```txt
|
25 | failed to open file `does not exist.txt`
|
26 | caused by: The system cannot find the file specified. (os error 2)
|
27 | ```
|
28 |
|
29 | # Usage
|
30 |
|
31 | fs-err's API is the same as [`std::fs`][std::fs], so migrating code to use it is easy.
|
32 |
|
33 | ```no_run
|
34 | // use std::fs;
|
35 | use fs_err as fs;
|
36 |
|
37 | let contents = fs::read_to_string("foo.txt" )?;
|
38 |
|
39 | println!("Read foo.txt: {}" , contents);
|
40 |
|
41 | # Ok::<(), std::io::Error>(())
|
42 | ```
|
43 |
|
44 | fs-err uses [`std::io::Error`][std::io::Error] for all errors. This helps fs-err
|
45 | compose well with traits from the standard library like
|
46 | [`std::io::Read`][std::io::Read] and crates that use them like
|
47 | [`serde_json`][serde_json]:
|
48 |
|
49 | ```no_run
|
50 | use fs_err::File;
|
51 |
|
52 | let file = File::open("my-config.json" )?;
|
53 |
|
54 | // If an I/O error occurs inside serde_json, the error will include a file path
|
55 | // as well as what operation was being performed.
|
56 | let decoded: Vec<String> = serde_json::from_reader(file)?;
|
57 |
|
58 | println!("Program config: {:?}" , decoded);
|
59 |
|
60 | # Ok::<(), Box<dyn std::error::Error>>(())
|
61 | ```
|
62 |
|
63 | [std::fs]: https://doc.rust-lang.org/stable/std/fs/
|
64 | [std::io::Error]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
|
65 | [std::io::Read]: https://doc.rust-lang.org/stable/std/io/trait.Read.html
|
66 | [serde_json]: https://crates.io/crates/serde_json
|
67 | */
|
68 |
|
69 | #![doc (html_root_url = "https://docs.rs/fs-err/2.9.0" )]
|
70 | #![deny (missing_debug_implementations, missing_docs)]
|
71 | #![cfg_attr (docsrs, feature(doc_cfg))]
|
72 |
|
73 | mod dir;
|
74 | mod errors;
|
75 | mod file;
|
76 | mod open_options;
|
77 | pub mod os;
|
78 | mod path;
|
79 | #[cfg (feature = "tokio" )]
|
80 | #[cfg_attr (docsrs, doc(cfg(feature = "tokio" )))]
|
81 | pub mod tokio;
|
82 |
|
83 | use std::fs;
|
84 | use std::io::{self, Read, Write};
|
85 | use std::path::{Path, PathBuf};
|
86 |
|
87 | use errors::{Error, ErrorKind, SourceDestError, SourceDestErrorKind};
|
88 |
|
89 | pub use dir::*;
|
90 | pub use file::*;
|
91 | pub use open_options::OpenOptions;
|
92 | pub use path::PathExt;
|
93 |
|
94 | /// Wrapper for [`fs::read`](https://doc.rust-lang.org/stable/std/fs/fn.read.html).
|
95 | pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
|
96 | let path: &Path = path.as_ref();
|
97 | let mut file: File = file::open(path).map_err(|err_gen: impl FnOnce(PathBuf) -> Error| err_gen(path.to_path_buf()))?;
|
98 | let mut bytes: Vec = Vec::with_capacity(initial_buffer_size(&file));
|
99 | file.read_to_end(&mut bytes)
|
100 | .map_err(|err: Error| Error::build(source:err, kind:ErrorKind::Read, path))?;
|
101 | Ok(bytes)
|
102 | }
|
103 |
|
104 | /// Wrapper for [`fs::read_to_string`](https://doc.rust-lang.org/stable/std/fs/fn.read_to_string.html).
|
105 | pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
|
106 | let path: &Path = path.as_ref();
|
107 | let mut file: File = file::open(path).map_err(|err_gen: impl FnOnce(PathBuf) -> Error| err_gen(path.to_path_buf()))?;
|
108 | let mut string: String = String::with_capacity(initial_buffer_size(&file));
|
109 | file.read_to_string(&mut string)
|
110 | .map_err(|err: Error| Error::build(source:err, kind:ErrorKind::Read, path))?;
|
111 | Ok(string)
|
112 | }
|
113 |
|
114 | /// Wrapper for [`fs::write`](https://doc.rust-lang.org/stable/std/fs/fn.write.html).
|
115 | pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
|
116 | let path: &Path = path.as_ref();
|
117 | file::create(path)
|
118 | .map_err(|err_gen| err_gen(path.to_path_buf()))?
|
119 | .write_all(contents.as_ref())
|
120 | .map_err(|err: Error| Error::build(source:err, kind:ErrorKind::Write, path))
|
121 | }
|
122 |
|
123 | /// Wrapper for [`fs::copy`](https://doc.rust-lang.org/stable/std/fs/fn.copy.html).
|
124 | pub fn copy<P, Q>(from: P, to: Q) -> io::Result<u64>
|
125 | where
|
126 | P: AsRef<Path>,
|
127 | Q: AsRef<Path>,
|
128 | {
|
129 | let from: &Path = from.as_ref();
|
130 | let to: &Path = to.as_ref();
|
131 | fs::copy(from, to)
|
132 | .map_err(|source: Error| SourceDestError::build(source, kind:SourceDestErrorKind::Copy, from_path:from, to_path:to))
|
133 | }
|
134 |
|
135 | /// Wrapper for [`fs::create_dir`](https://doc.rust-lang.org/stable/std/fs/fn.create_dir.html).
|
136 | pub fn create_dir<P>(path: P) -> io::Result<()>
|
137 | where
|
138 | P: AsRef<Path>,
|
139 | {
|
140 | let path: &Path = path.as_ref();
|
141 | fs::create_dir(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::CreateDir, path))
|
142 | }
|
143 |
|
144 | /// Wrapper for [`fs::create_dir_all`](https://doc.rust-lang.org/stable/std/fs/fn.create_dir_all.html).
|
145 | pub fn create_dir_all<P>(path: P) -> io::Result<()>
|
146 | where
|
147 | P: AsRef<Path>,
|
148 | {
|
149 | let path: &Path = path.as_ref();
|
150 | fs::create_dir_all(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::CreateDir, path))
|
151 | }
|
152 |
|
153 | /// Wrapper for [`fs::remove_dir`](https://doc.rust-lang.org/stable/std/fs/fn.remove_dir.html).
|
154 | pub fn remove_dir<P>(path: P) -> io::Result<()>
|
155 | where
|
156 | P: AsRef<Path>,
|
157 | {
|
158 | let path: &Path = path.as_ref();
|
159 | fs::remove_dir(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::RemoveDir, path))
|
160 | }
|
161 |
|
162 | /// Wrapper for [`fs::remove_dir_all`](https://doc.rust-lang.org/stable/std/fs/fn.remove_dir_all.html).
|
163 | pub fn remove_dir_all<P>(path: P) -> io::Result<()>
|
164 | where
|
165 | P: AsRef<Path>,
|
166 | {
|
167 | let path: &Path = path.as_ref();
|
168 | fs::remove_dir_all(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::RemoveDir, path))
|
169 | }
|
170 |
|
171 | /// Wrapper for [`fs::remove_file`](https://doc.rust-lang.org/stable/std/fs/fn.remove_file.html).
|
172 | pub fn remove_file<P>(path: P) -> io::Result<()>
|
173 | where
|
174 | P: AsRef<Path>,
|
175 | {
|
176 | let path: &Path = path.as_ref();
|
177 | fs::remove_file(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::RemoveFile, path))
|
178 | }
|
179 |
|
180 | /// Wrapper for [`fs::metadata`](https://doc.rust-lang.org/stable/std/fs/fn.metadata.html).
|
181 | pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<fs::Metadata> {
|
182 | let path: &Path = path.as_ref();
|
183 | fs::metadata(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::Metadata, path))
|
184 | }
|
185 |
|
186 | /// Wrapper for [`fs::canonicalize`](https://doc.rust-lang.org/stable/std/fs/fn.canonicalize.html).
|
187 | pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
188 | let path: &Path = path.as_ref();
|
189 | fs::canonicalize(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::Canonicalize, path))
|
190 | }
|
191 |
|
192 | /// Wrapper for [`fs::hard_link`](https://doc.rust-lang.org/stable/std/fs/fn.hard_link.html).
|
193 | pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
194 | let src: &Path = src.as_ref();
|
195 | let dst: &Path = dst.as_ref();
|
196 | fs::hard_link(src, dst)
|
197 | .map_err(|source: Error| SourceDestError::build(source, kind:SourceDestErrorKind::HardLink, from_path:src, to_path:dst))
|
198 | }
|
199 |
|
200 | /// Wrapper for [`fs::read_link`](https://doc.rust-lang.org/stable/std/fs/fn.read_link.html).
|
201 | pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
|
202 | let path: &Path = path.as_ref();
|
203 | fs::read_link(path).map_err(|source: Error| Error::build(source, kind:ErrorKind::ReadLink, path))
|
204 | }
|
205 |
|
206 | /// Wrapper for [`fs::rename`](https://doc.rust-lang.org/stable/std/fs/fn.rename.html).
|
207 | pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
208 | let from: &Path = from.as_ref();
|
209 | let to: &Path = to.as_ref();
|
210 | fs::rename(from, to)
|
211 | .map_err(|source: Error| SourceDestError::build(source, kind:SourceDestErrorKind::Rename, from_path:from, to_path:to))
|
212 | }
|
213 |
|
214 | /// Wrapper for [`fs::soft_link`](https://doc.rust-lang.org/stable/std/fs/fn.soft_link.html).
|
215 | #[deprecated = "replaced with std::os::unix::fs::symlink and \
|
216 | std::os::windows::fs::{symlink_file, symlink_dir}" ]
|
217 | pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
218 | let src: &Path = src.as_ref();
|
219 | let dst: &Path = dst.as_ref();
|
220 | #[allow (deprecated)]
|
221 | fs::soft_link(src, dst)
|
222 | .map_err(|source: Error| SourceDestError::build(source, kind:SourceDestErrorKind::SoftLink, from_path:src, to_path:dst))
|
223 | }
|
224 |
|
225 | /// Wrapper for [`fs::symlink_metadata`](https://doc.rust-lang.org/stable/std/fs/fn.symlink_metadata.html).
|
226 | pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<fs::Metadata> {
|
227 | let path: &Path = path.as_ref();
|
228 | fs::symlink_metadata(path)
|
229 | .map_err(|source: Error| Error::build(source, kind:ErrorKind::SymlinkMetadata, path))
|
230 | }
|
231 |
|
232 | /// Wrapper for [`fs::set_permissions`](https://doc.rust-lang.org/stable/std/fs/fn.set_permissions.html).
|
233 | pub fn set_permissions<P: AsRef<Path>>(path: P, perm: fs::Permissions) -> io::Result<()> {
|
234 | let path: &Path = path.as_ref();
|
235 | fs::set_permissions(path, perm)
|
236 | .map_err(|source: Error| Error::build(source, kind:ErrorKind::SetPermissions, path))
|
237 | }
|
238 |
|
239 | fn initial_buffer_size(file: &std::fs::File) -> usize {
|
240 | file.metadata().map(|m| m.len() as usize + 1).unwrap_or(default:0)
|
241 | }
|
242 |
|
243 | pub(crate) use private::Sealed;
|
244 | mod private {
|
245 | pub trait Sealed {}
|
246 |
|
247 | impl Sealed for crate::File {}
|
248 | impl Sealed for std::path::Path {}
|
249 | impl Sealed for crate::OpenOptions {}
|
250 | }
|
251 | |