1/*!
2fs-err is a drop-in replacement for [`std::fs`][std::fs] that provides more
3helpful messages on errors. Extra information includes which operations was
4attempted and any involved paths.
5
6# Error Messages
7
8Using [`std::fs`][std::fs], if this code fails:
9
10```no_run
11# use std::fs::File;
12let file = File::open("does not exist.txt")?;
13# Ok::<(), std::io::Error>(())
14```
15
16The error message that Rust gives you isn't very useful:
17
18```txt
19The 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
25failed 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
31fs-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;
35use fs_err as fs;
36
37let contents = fs::read_to_string("foo.txt")?;
38
39println!("Read foo.txt: {}", contents);
40
41# Ok::<(), std::io::Error>(())
42```
43
44fs-err uses [`std::io::Error`][std::io::Error] for all errors. This helps fs-err
45compose 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
50use fs_err::File;
51
52let 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.
56let decoded: Vec<String> = serde_json::from_reader(file)?;
57
58println!("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
73mod dir;
74mod errors;
75mod file;
76mod open_options;
77pub mod os;
78mod path;
79#[cfg(feature = "tokio")]
80#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
81pub mod tokio;
82
83use std::fs;
84use std::io::{self, Read, Write};
85use std::path::{Path, PathBuf};
86
87use errors::{Error, ErrorKind, SourceDestError, SourceDestErrorKind};
88
89pub use dir::*;
90pub use file::*;
91pub use open_options::OpenOptions;
92pub use path::PathExt;
93
94/// Wrapper for [`fs::read`](https://doc.rust-lang.org/stable/std/fs/fn.read.html).
95pub 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).
105pub 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).
115pub 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).
124pub fn copy<P, Q>(from: P, to: Q) -> io::Result<u64>
125where
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).
136pub fn create_dir<P>(path: P) -> io::Result<()>
137where
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).
145pub fn create_dir_all<P>(path: P) -> io::Result<()>
146where
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).
154pub fn remove_dir<P>(path: P) -> io::Result<()>
155where
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).
163pub fn remove_dir_all<P>(path: P) -> io::Result<()>
164where
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).
172pub fn remove_file<P>(path: P) -> io::Result<()>
173where
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).
181pub 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).
187pub 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).
193pub 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).
201pub 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).
207pub 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 \
216std::os::windows::fs::{symlink_file, symlink_dir}"]
217pub 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).
226pub 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).
233pub 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
239fn initial_buffer_size(file: &std::fs::File) -> usize {
240 file.metadata().map(|m| m.len() as usize + 1).unwrap_or(default:0)
241}
242
243pub(crate) use private::Sealed;
244mod 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